Commit 1eec3cd3 authored by Wei Han's avatar Wei Han

code update

parent 61a8bfd5
......@@ -6,7 +6,7 @@
<dict>
<key>Assets.car</key>
<data>
KnMHH8GU4NZESWbs6/0uU5WX8uA=
EqtjfGVJUrKupCnFW6lfffSo2ZA=
</data>
<key>Headers/QmsPlugin.h</key>
<data>
......@@ -18,7 +18,7 @@
</data>
<key>Info.plist</key>
<data>
ScoY0XAgmZ3U9Q0BvlfgQfJm2bo=
w8N5kkR8hr/wIwb/x0XA5+wVpdo=
</data>
<key>Modules/module.modulemap</key>
<data>
......@@ -31,7 +31,7 @@
<dict>
<key>hash2</key>
<data>
aLAhYopvL1Lb4CTMG5A8x8RAmnkpXwHQbjsJdpq8gYg=
k78SvGog/XWiM8sEWRQ8ZyFHnocCgVMjS4SisBwChjI=
</data>
</dict>
<key>Headers/QmsPlugin.h</key>
......
......@@ -6,7 +6,7 @@
<dict>
<key>Assets.car</key>
<data>
KnMHH8GU4NZESWbs6/0uU5WX8uA=
EqtjfGVJUrKupCnFW6lfffSo2ZA=
</data>
<key>Headers/QmsPlugin.h</key>
<data>
......@@ -18,7 +18,7 @@
</data>
<key>Info.plist</key>
<data>
JGviPenfphujtQEGyJZCIZTsd2c=
uEkE+l7bQzwu7vnX83uBlOCEpCk=
</data>
<key>Modules/module.modulemap</key>
<data>
......@@ -31,7 +31,7 @@
<dict>
<key>hash2</key>
<data>
aLAhYopvL1Lb4CTMG5A8x8RAmnkpXwHQbjsJdpq8gYg=
k78SvGog/XWiM8sEWRQ8ZyFHnocCgVMjS4SisBwChjI=
</data>
</dict>
<key>Headers/QmsPlugin.h</key>
......
......@@ -20,7 +20,26 @@
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor systemGroupedBackgroundColor];
// --- Base background (replaces systemGroupedBackground) ---
self.view.backgroundColor = [UIColor whiteColor];
// --- Header bar color ---
self.headerView.backgroundColor = [UIColor colorWithRed:0.0 green:0.43 blue:0.55 alpha:1.0]; // teal
self.titleLabel.textColor = [UIColor whiteColor];
// --- TableView styling ---
self.tableView.backgroundColor = [UIColor colorWithWhite:0.97 alpha:1.0]; // very light gray
self.tableView.separatorColor = [UIColor colorWithWhite:0.9 alpha:1.0];
// --- Section header label colors ---
self.tableView.sectionIndexColor = [UIColor darkGrayColor];
// --- Optional: consistent scroll indicator style ---
self.tableView.indicatorStyle = UIScrollViewIndicatorStyleBlack;
// --- Tint / global accent color (used for buttons, etc) ---
self.view.tintColor = [UIColor colorWithRed:0.0 green:0.43 blue:0.55 alpha:1.0];
[self setupHeader];
[self setupTableView];
......@@ -36,7 +55,8 @@
#pragma mark - Header
- (void)setupHeader {
self.headerView = [[UIView alloc] init];
CGFloat statusBarHeight = UIApplication.sharedApplication.statusBarFrame.size.height;
self.headerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, statusBarHeight + 60)];
self.headerView.backgroundColor = [UIColor colorWithRed:0.0 green:0.43 blue:0.55 alpha:1.0];
self.headerView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:self.headerView];
......@@ -56,13 +76,6 @@
self.titleLabel.translatesAutoresizingMaskIntoConstraints = NO;
[self.headerView addSubview:self.titleLabel];
self.addInfoButton = [UIButton buttonWithType:UIButtonTypeSystem];
[self.addInfoButton setImage:[UIImage systemImageNamed:@"plus"] forState:UIControlStateNormal];
self.addInfoButton.tintColor = UIColor.whiteColor;
self.addInfoButton.translatesAutoresizingMaskIntoConstraints = NO;
[self.addInfoButton addTarget:self action:@selector(addInfoTapped) forControlEvents:UIControlEventTouchUpInside];
[self.headerView addSubview:self.addInfoButton];
[NSLayoutConstraint activateConstraints:@[
[self.headerView.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor],
[self.headerView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
......@@ -72,9 +85,6 @@
[self.backButton.leadingAnchor constraintEqualToAnchor:self.headerView.leadingAnchor constant:12],
[self.backButton.centerYAnchor constraintEqualToAnchor:self.headerView.centerYAnchor],
[self.addInfoButton.trailingAnchor constraintEqualToAnchor:self.headerView.trailingAnchor constant:-12],
[self.addInfoButton.centerYAnchor constraintEqualToAnchor:self.headerView.centerYAnchor],
[self.titleLabel.centerXAnchor constraintEqualToAnchor:self.headerView.centerXAnchor],
[self.titleLabel.centerYAnchor constraintEqualToAnchor:self.headerView.centerYAnchor],
]];
......@@ -87,6 +97,9 @@
self.tableView.delegate = self;
self.tableView.dataSource = self;
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
self.tableView.backgroundView = nil;
self.tableView.backgroundColor = [UIColor whiteColor];
self.tableView.opaque = YES;
[self.view addSubview:self.tableView];
[NSLayoutConstraint activateConstraints:@[
......@@ -186,6 +199,9 @@
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
cell.backgroundColor = [UIColor clearColor];
cell.contentView.backgroundColor = [UIColor clearColor];
UIView *card = [[UIView alloc] init];
card.tag = 100;
......
......@@ -14,6 +14,7 @@
@property (nonatomic, strong) UIView *detailsCard;
@property (nonatomic, strong) UIButton *editButton;
@property (nonatomic, strong) UIButton *deleteButton;
@property (nonatomic, strong) UIImageView *floorPlanThumbnail;
@end
......@@ -150,11 +151,33 @@
CGFloat y = 20;
// --- Reference ID ---
self.refLabel = [[UILabel alloc] initWithFrame:CGRectMake(padding, y, self.view.bounds.size.width - 2*padding, 24)];
CGFloat thumbWidth = 40;
CGFloat gap = 12;
self.refLabel = [[UILabel alloc] initWithFrame:CGRectMake(padding, y, self.view.bounds.size.width - 2*padding - thumbWidth - gap, 24)];
self.refLabel.font = [UIFont boldSystemFontOfSize:20];
self.refLabel.textColor = UIColor.blackColor; // make sure it’s visible
self.refLabel.numberOfLines = 1;
self.refLabel.adjustsFontSizeToFitWidth = YES;
[self.detailsCard addSubview:self.refLabel];
y += 36;
// --- Floor Plan Thumbnail ---
CGFloat thumbSize = 100; // new size
self.floorPlanThumbnail = [[UIImageView alloc] initWithFrame:
CGRectMake(self.view.bounds.size.width - padding - thumbSize, 20, thumbSize, thumbSize)];
self.floorPlanThumbnail.contentMode = UIViewContentModeScaleAspectFill;
self.floorPlanThumbnail.clipsToBounds = YES;
self.floorPlanThumbnail.layer.cornerRadius = 4;
self.floorPlanThumbnail.backgroundColor = [UIColor colorWithWhite:0.9 alpha:1];
self.floorPlanThumbnail.layer.borderColor = [UIColor systemGray3Color].CGColor;
self.floorPlanThumbnail.layer.borderWidth = 1.0;
self.floorPlanThumbnail.userInteractionEnabled = YES;
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleFloorPlanTap)];
[self.floorPlanThumbnail addGestureRecognizer:tap];
[self.detailsCard addSubview:self.floorPlanThumbnail];
// --- Status Capsule ---
self.statusLabel = [[UILabel alloc] initWithFrame:CGRectMake(padding, y, 90, 28)];
self.statusLabel.font = [UIFont boldSystemFontOfSize:14];
......@@ -341,6 +364,20 @@
}
});
}
NSString *planThumbURL = self.issueData[@"plan_image_thumb"];
if (planThumbURL.length > 0) {
NSURL *url = [NSURL URLWithString:planThumbURL];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData *data = [NSData dataWithContentsOfURL:url];
if (data) {
UIImage *img = [UIImage imageWithData:data];
dispatch_async(dispatch_get_main_queue(), ^{
self.floorPlanThumbnail.image = img;
});
}
});
}
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
......@@ -378,7 +415,6 @@
}];
}
- (void)closeFullImage {
UIView *overlay = [self.view viewWithTag:900];
if (!overlay) return;
......@@ -390,6 +426,53 @@
}];
}
- (void)handleFloorPlanTap {
NSString *planFullURL = self.issueData[@"plan_image"];
if (planFullURL.length == 0) return; // no full image available
UIView *overlay = [[UIView alloc] initWithFrame:self.view.bounds];
overlay.backgroundColor = [UIColor colorWithWhite:0 alpha:0.95];
overlay.alpha = 0;
overlay.tag = 901;
overlay.userInteractionEnabled = YES;
[self.view addSubview:overlay];
UIImageView *fullImageView = [[UIImageView alloc] initWithFrame:overlay.bounds];
fullImageView.contentMode = UIViewContentModeScaleAspectFit;
fullImageView.userInteractionEnabled = YES;
[overlay addSubview:fullImageView];
// Fetch the full-resolution image asynchronously
NSURL *url = [NSURL URLWithString:planFullURL];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData *data = [NSData dataWithContentsOfURL:url];
if (data) {
UIImage *img = [UIImage imageWithData:data];
dispatch_async(dispatch_get_main_queue(), ^{
fullImageView.image = img;
});
}
});
UITapGestureRecognizer *dismissTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(closeFullFloorPlan)];
[overlay addGestureRecognizer:dismissTap];
[UIView animateWithDuration:0.25 animations:^{
overlay.alpha = 1;
}];
}
- (void)closeFullFloorPlan {
UIView *overlay = [self.view viewWithTag:901];
if (!overlay) return;
[UIView animateWithDuration:0.25 animations:^{
overlay.alpha = 0;
} completion:^(BOOL finished) {
[overlay removeFromSuperview];
}];
}
#pragma mark - Helper
- (UIColor *)colorFromHex:(NSString *)hexString {
if (!hexString || hexString.length < 6) return [UIColor grayColor];
......@@ -414,7 +497,7 @@
HistoryViewController *nextVC = [[HistoryViewController alloc] init];
nextVC.modalPresentationStyle = UIModalPresentationFullScreen;
nextVC.issueID = self.issueData[@"issue_id"] ?: @"";
nextVC.issueID = [NSString stringWithFormat:@"%@", self.issueData[@"issue_reference_id"] ?: self.issueData[@"id"] ?: @""];
nextVC.planID = self.issueData[@"plan_id"];
nextVC.status = self.issueData[@"status_external"];
nextVC.unitName = self.issueData[@"plan_unit"];
......
......@@ -25,12 +25,13 @@
self.backgroundColor = UIColor.clearColor;
UIView *card = [[UIView alloc] init];
card.backgroundColor = UIColor.whiteColor;
card.layer.cornerRadius = 12;
card.layer.shadowColor = [UIColor colorWithWhite:0 alpha:0.15].CGColor;
card.layer.shadowOpacity = 0.1;
card.layer.shadowRadius = 3;
card.layer.borderWidth = 1.0;
card.layer.borderColor = [UIColor colorWithWhite:0.85 alpha:1.0].CGColor;
card.layer.shadowColor = [UIColor colorWithWhite:0.0 alpha:0.2].CGColor;
card.layer.shadowOpacity = 0.25;
card.layer.shadowRadius = 4;
card.layer.shadowOffset = CGSizeMake(0, 2);
card.layer.masksToBounds = NO;
card.translatesAutoresizingMaskIntoConstraints = NO;
[self.contentView addSubview:card];
......@@ -137,7 +138,7 @@
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor systemGroupedBackgroundColor];
self.view.backgroundColor = [UIColor whiteColor];
[self setupHeader];
[self setupFilterBar];
[self setupSearchBar];
......@@ -153,7 +154,8 @@
#pragma mark - Setup UI
- (void)setupHeader {
self.headerView = [[UIView alloc] init];
CGFloat statusBarHeight = UIApplication.sharedApplication.statusBarFrame.size.height;
self.headerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, statusBarHeight + 60)];
self.headerView.translatesAutoresizingMaskIntoConstraints = NO;
self.headerView.backgroundColor = [UIColor colorWithRed:0.0 green:0.43 blue:0.55 alpha:1.0]; // THEME COLOR
[self.view addSubview:self.headerView];
......@@ -176,7 +178,7 @@
// Withdraw Button
self.withdrawButton = [UIButton buttonWithType:UIButtonTypeSystem];
[self.withdrawButton setImage:[UIImage systemImageNamed:@"tray.and.arrow.down"] forState:UIControlStateNormal];
[self.withdrawButton setImage:[UIImage systemImageNamed:@"checklist.checked"] forState:UIControlStateNormal];
self.withdrawButton.tintColor = UIColor.whiteColor;
self.withdrawButton.translatesAutoresizingMaskIntoConstraints = NO;
[self.withdrawButton addTarget:self action:@selector(withdrawTapped) forControlEvents:UIControlEventTouchUpInside];
......@@ -190,6 +192,7 @@
self.filterButton.layer.borderWidth = 0.5;
self.filterButton.layer.borderColor = [UIColor lightGrayColor].CGColor;
[self.filterButton setTitle:@"Filter: All" forState:UIControlStateNormal];
[self.filterButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
self.filterButton.tintColor = UIColor.labelColor;
self.filterButton.backgroundColor = UIColor.whiteColor;
self.filterButton.titleLabel.font = [UIFont systemFontOfSize:15];
......@@ -205,6 +208,35 @@
self.searchBar.autocapitalizationType = UITextAutocapitalizationTypeNone;
self.searchBar.autocorrectionType = UITextAutocorrectionTypeNo;
self.searchBar.returnKeyType = UIReturnKeyDone;
self.searchBar.showsCancelButton = YES;
self.searchBar.barTintColor = [UIColor whiteColor];
self.searchBar.backgroundImage = [UIImage new];
self.searchBar.opaque = YES;
// ✅ Customize the internal text field
UITextField *textField = [self.searchBar valueForKey:@"searchField"];
if (textField) {
textField.textColor = [UIColor blackColor];
textField.tintColor = [UIColor darkGrayColor];
textField.backgroundColor = [UIColor whiteColor];
textField.layer.borderColor = [UIColor lightGrayColor].CGColor;
textField.layer.borderWidth = 1.0;
textField.layer.cornerRadius = 8.0;
textField.clipsToBounds = YES;
textField.attributedPlaceholder = [[NSAttributedString alloc]
initWithString:self.searchBar.placeholder
attributes:@{NSForegroundColorAttributeName: [UIColor grayColor]}];
}
UIButton *cancelButton = [self.searchBar valueForKey:@"cancelButton"];
if (cancelButton) {
[cancelButton setTitleColor:[UIColor systemBlueColor] forState:UIControlStateNormal];
[cancelButton setTitleColor:[UIColor grayColor] forState:UIControlStateHighlighted];
cancelButton.titleLabel.font = [UIFont boldSystemFontOfSize:16];
}
[self.view addSubview:self.searchBar];
}
......@@ -213,6 +245,9 @@
self.tableView.translatesAutoresizingMaskIntoConstraints = NO;
self.tableView.delegate = self;
self.tableView.dataSource = self;
self.tableView.backgroundView = nil;
self.tableView.backgroundColor = [UIColor whiteColor];
self.tableView.opaque = YES;
[self.tableView registerClass:[IssueCell class] forCellReuseIdentifier:@"IssueCell"];
// 🔹 Section header behavior
......@@ -332,10 +367,21 @@
NSString *reference = [issue[@"reference"] ?: @"" uppercaseString];
NSString *location = [issue[@"location_name"] ?: @"" uppercaseString];
NSString *remarks = [issue[@"remarks"] ?: @"" uppercaseString];
NSString *createdAt = [issue[@"created_at"] ?: @"" uppercaseString];
NSString *category = [issue[@"category"] ?: @"" uppercaseString];
NSString *type = [issue[@"type"] ?: @"" uppercaseString];
NSString *issueText = [issue[@"issue"] ?: @"" uppercaseString];
NSString *status = [issue[@"status_external"] ?: @"" uppercaseString];
if ([reference containsString:search] ||
[location containsString:search] ||
[remarks containsString:search]) {
[remarks containsString:search] ||
[createdAt containsString:search] ||
[category containsString:search] ||
[type containsString:search] ||
[issueText containsString:search] ||
[status containsString:search]) {
[matches addObject:issue];
}
}
......@@ -678,4 +724,21 @@
[self presentViewController:detailVC animated:YES completion:nil];
}
#pragma mark - UISearchBarDelegate
-(void)searchbar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
[self applySearch:searchText];
}
-(void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
[searchBar resignFirstResponder];
[self applySearch:searchBar.text];
}
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
// Optional: reset search when user cancels
[searchBar resignFirstResponder];
searchBar.text = @"";
[self applySearch:@""];
}
@end
......@@ -3,9 +3,153 @@
#import "IssueAPIClient.h"
#import "WithdrawSingleIssueViewController.h"
#pragma mark - custom cell structure
@interface WithdrawIssueCell : UITableViewCell
@property (nonatomic, strong) UIImageView *thumbView;
@property (nonatomic, strong) UILabel *referenceLabel;
@property (nonatomic, strong) UILabel *statusLabel;
@property (nonatomic, strong) UILabel *locationLabel;
@property (nonatomic, strong) UILabel *createdLabel;
@property (nonatomic, strong) UILabel *detailsLabel;
@property (nonatomic, strong) UIImageView *checkmarkView;
@end
@implementation WithdrawIssueCell
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
self.backgroundColor = UIColor.clearColor;
self.selectionStyle = UITableViewCellSelectionStyleNone;
UIView *card = [[UIView alloc] init];
card.backgroundColor = UIColor.whiteColor;
card.layer.cornerRadius = 12;
card.layer.shadowColor = [UIColor blackColor].CGColor;
card.layer.shadowOpacity = 0.1;
card.layer.shadowOffset = CGSizeMake(0, 2);
card.layer.shadowRadius = 4;
card.translatesAutoresizingMaskIntoConstraints = NO;
[self.contentView addSubview:card];
// Tick/checkmark icon
_checkmarkView = [[UIImageView alloc] initWithImage:[UIImage systemImageNamed:@"circle"]];
_checkmarkView.tintColor = [UIColor lightGrayColor];
_checkmarkView.translatesAutoresizingMaskIntoConstraints = NO;
_checkmarkView.translatesAutoresizingMaskIntoConstraints = NO;
[self.contentView addSubview:_checkmarkView];
// Thumbnail
_thumbView = [[UIImageView alloc] init];
_thumbView.translatesAutoresizingMaskIntoConstraints = NO;
_thumbView.layer.cornerRadius = 25;
_thumbView.layer.masksToBounds = YES;
_thumbView.contentMode = UIViewContentModeScaleAspectFill;
[card addSubview:_thumbView];
// Labels
_referenceLabel = [[UILabel alloc] init];
_referenceLabel.font = [UIFont boldSystemFontOfSize:16];
_referenceLabel.translatesAutoresizingMaskIntoConstraints = NO;
[card addSubview:_referenceLabel];
_statusLabel = [[UILabel alloc] init];
_statusLabel.font = [UIFont systemFontOfSize:12 weight:UIFontWeightMedium];
_statusLabel.textColor = UIColor.whiteColor;
_statusLabel.backgroundColor = [UIColor systemTealColor];
_statusLabel.layer.cornerRadius = 6;
_statusLabel.layer.masksToBounds = YES;
_statusLabel.textAlignment = NSTextAlignmentCenter;
_statusLabel.translatesAutoresizingMaskIntoConstraints = NO;
[card addSubview:_statusLabel];
_locationLabel = [[UILabel alloc] init];
_locationLabel.font = [UIFont systemFontOfSize:14];
_locationLabel.textColor = UIColor.darkGrayColor;
_locationLabel.translatesAutoresizingMaskIntoConstraints = NO;
[card addSubview:_locationLabel];
_createdLabel = [[UILabel alloc] init];
_createdLabel.font = [UIFont systemFontOfSize:12];
_createdLabel.textColor = UIColor.grayColor;
_createdLabel.translatesAutoresizingMaskIntoConstraints = NO;
[card addSubview:_createdLabel];
_detailsLabel = [[UILabel alloc] init];
_detailsLabel.font = [UIFont systemFontOfSize:13];
_detailsLabel.textColor = [UIColor grayColor];
_detailsLabel.numberOfLines = 2;
_detailsLabel.translatesAutoresizingMaskIntoConstraints = NO;
[card addSubview:_detailsLabel];
// Layout
[NSLayoutConstraint activateConstraints:@[
// Checkmark
[_checkmarkView.leadingAnchor constraintEqualToAnchor:self.contentView.leadingAnchor constant:10],
[_checkmarkView.centerYAnchor constraintEqualToAnchor:self.contentView.centerYAnchor],
[_checkmarkView.widthAnchor constraintEqualToConstant:28],
[_checkmarkView.heightAnchor constraintEqualToConstant:28],
// Card itself
[card.leadingAnchor constraintEqualToAnchor:_checkmarkView.trailingAnchor constant:6],
[card.trailingAnchor constraintEqualToAnchor:self.contentView.trailingAnchor constant:-8],
[card.topAnchor constraintEqualToAnchor:self.contentView.topAnchor constant:4],
[card.bottomAnchor constraintEqualToAnchor:self.contentView.bottomAnchor constant:-4],
// Thumbnail
[_thumbView.leadingAnchor constraintEqualToAnchor:card.leadingAnchor constant:12],
[_thumbView.topAnchor constraintEqualToAnchor:card.topAnchor constant:12],
[_thumbView.widthAnchor constraintEqualToConstant:50],
[_thumbView.heightAnchor constraintEqualToConstant:50],
// Reference
[_referenceLabel.topAnchor constraintEqualToAnchor:card.topAnchor constant:12],
[_referenceLabel.leadingAnchor constraintEqualToAnchor:_thumbView.trailingAnchor constant:12],
[_referenceLabel.trailingAnchor constraintLessThanOrEqualToAnchor:_statusLabel.leadingAnchor constant:-8],
// Status
[_statusLabel.centerYAnchor constraintEqualToAnchor:_locationLabel.centerYAnchor],
[_statusLabel.trailingAnchor constraintEqualToAnchor:card.trailingAnchor constant:-12],
[_statusLabel.widthAnchor constraintGreaterThanOrEqualToConstant:60],
[_statusLabel.heightAnchor constraintEqualToConstant:22],
// Location
[_locationLabel.topAnchor constraintEqualToAnchor:_referenceLabel.bottomAnchor constant:4],
[_locationLabel.leadingAnchor constraintEqualToAnchor:_thumbView.trailingAnchor constant:12],
[_locationLabel.trailingAnchor constraintEqualToAnchor:card.trailingAnchor constant:-12],
// Created
[_createdLabel.topAnchor constraintEqualToAnchor:_locationLabel.bottomAnchor constant:4],
[_createdLabel.leadingAnchor constraintEqualToAnchor:_thumbView.trailingAnchor constant:12],
[_createdLabel.trailingAnchor constraintEqualToAnchor:card.trailingAnchor constant:-12],
// Details (at bottom)
[_detailsLabel.topAnchor constraintEqualToAnchor:_createdLabel.bottomAnchor constant:4],
[_detailsLabel.leadingAnchor constraintEqualToAnchor:_thumbView.trailingAnchor constant:12],
[_detailsLabel.trailingAnchor constraintEqualToAnchor:card.trailingAnchor constant:-12],
[_detailsLabel.bottomAnchor constraintEqualToAnchor:card.bottomAnchor constant:-12],
]];
}
return self;
}
- (void)updateSelectionState:(BOOL)isSelected {
self.checkmarkView.image = isSelected
? [UIImage systemImageNamed:@"checkmark.circle.fill"]
: [UIImage systemImageNamed:@"circle"];
self.checkmarkView.tintColor = isSelected
? [UIColor systemTealColor]
: [UIColor lightGrayColor];
self.contentView.alpha = isSelected ? 0.85 : 1.0;
}
@end
#pragma mark - view controller
@interface WithdrawIssuesViewController () <UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate>
@property (nonatomic, strong) UIButton *closeButton;
@property (nonatomic, strong) UIButton *backButton;
@property (nonatomic, strong) UILabel *titleLabel;
@property (nonatomic, strong) UISearchBar *searchBar;
@property (nonatomic, strong) UIButton *filterButton;
......@@ -13,8 +157,7 @@
@property (nonatomic, strong) UITableView *tableView;
@property (nonatomic, strong) UILabel *counterLabel;
@property (nonatomic, strong) UIButton *proceedButton;
@property (nonatomic, strong) UIView *searchContainer;
@property (nonatomic, strong) UIView *bottomBar;
@property (nonatomic, strong) NSArray *issues;
@property (nonatomic, strong) NSMutableSet *selectedIssueIDs;
@property (nonatomic, strong) NSArray *filteredIssues;
......@@ -25,7 +168,7 @@
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = UIColor.systemGroupedBackgroundColor;
self.view.backgroundColor = UIColor.whiteColor;
self.selectedIssueIDs = [NSMutableSet set];
[self setupHeader];
......@@ -35,19 +178,47 @@
[self fetchIssues];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
static BOOL hasLoadedBefore = NO;
if (!hasLoadedBefore) {
hasLoadedBefore = YES;
[self.selectedIssueIDs removeAllObjects];
[self.tableView reloadData];
} else if (self.isMovingToParentViewController) {
// Coming here via push (forward navigation)
[self.selectedIssueIDs removeAllObjects];
[self.tableView reloadData];
}
}
#pragma mark - UI Setup
- (void)setupHeader {
UIView *statusBarBackground = [[UIView alloc] init];
statusBarBackground.backgroundColor = [UIColor colorWithRed:0/255.0
green:109/255.0
blue:141/255.0
alpha:1];
statusBarBackground.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:statusBarBackground];
UIView *header = [[UIView alloc] init];
header.backgroundColor = [UIColor colorWithRed:0.0 green:0.43 blue:0.55 alpha:1.0];
header.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:header];
self.closeButton = [UIButton buttonWithType:UIButtonTypeSystem];
[self.closeButton setImage:[UIImage systemImageNamed:@"xmark"] forState:UIControlStateNormal];
self.closeButton.tintColor = UIColor.whiteColor;
[self.closeButton addTarget:self action:@selector(closeTapped) forControlEvents:UIControlEventTouchUpInside];
self.closeButton.translatesAutoresizingMaskIntoConstraints = NO;
[header addSubview:self.closeButton];
// Back button
self.backButton = [UIButton buttonWithType:UIButtonTypeSystem];
self.backButton.translatesAutoresizingMaskIntoConstraints = NO;
UIImage *backImage = [[UIImage systemImageNamed:@"chevron.left"]
imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
[self.backButton setImage:backImage forState:UIControlStateNormal];
self.backButton.tintColor = UIColor.whiteColor;
[self.backButton addTarget:self
action:@selector(handleBack)
forControlEvents:UIControlEventTouchUpInside];
[header addSubview:self.backButton];
self.titleLabel = [[UILabel alloc] init];
self.titleLabel.text = @"Withdraw Issue";
......@@ -62,8 +233,8 @@
[header.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
[header.heightAnchor constraintEqualToConstant:50],
[self.closeButton.leadingAnchor constraintEqualToAnchor:header.leadingAnchor constant:16],
[self.closeButton.centerYAnchor constraintEqualToAnchor:header.centerYAnchor],
[self.backButton.leadingAnchor constraintEqualToAnchor:header.leadingAnchor constant:16],
[self.backButton.centerYAnchor constraintEqualToAnchor:header.centerYAnchor],
[self.titleLabel.centerXAnchor constraintEqualToAnchor:header.centerXAnchor],
[self.titleLabel.centerYAnchor constraintEqualToAnchor:header.centerYAnchor],
......@@ -71,71 +242,120 @@
}
- (void)setupSearchBar {
self.searchContainer = [[UIView alloc] init]; // 👈 keep a property reference
self.searchContainer.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:self.searchContainer];
UIView *searchContainer = [[UIView alloc] init];
searchContainer.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:searchContainer];
self.searchBar = [[UISearchBar alloc] init];
self.searchBar.placeholder = @"Search Reference Number / Location";
self.searchBar.delegate = self;
self.searchBar.translatesAutoresizingMaskIntoConstraints = NO;
[self.searchContainer addSubview:self.searchBar];
self.searchBar.showsCancelButton = YES;
self.searchBar.barTintColor = [UIColor whiteColor];
self.searchBar.backgroundImage = [UIImage new]; // removes dark/blurred bg
if (@available(iOS 13.0, *)) {
self.searchBar.searchTextField.backgroundColor = [UIColor whiteColor];
self.searchBar.searchTextField.textColor = [UIColor blackColor];
}
self.searchBar.opaque = YES;
[searchContainer addSubview:self.searchBar];
self.selectAllButton = [UIButton buttonWithType:UIButtonTypeSystem];
[self.selectAllButton setTitle:@"Select All" forState:UIControlStateNormal];
[self.selectAllButton setTitleColor:[UIColor colorWithRed:0.0 green:0.43 blue:0.55 alpha:1.0] forState:UIControlStateNormal];
self.selectAllButton.translatesAutoresizingMaskIntoConstraints = NO;
[self.selectAllButton addTarget:self action:@selector(selectAllTapped) forControlEvents:UIControlEventTouchUpInside];
[self.searchContainer addSubview:self.selectAllButton];
UIView *header = self.view.subviews.firstObject;
[searchContainer addSubview:self.selectAllButton];
[NSLayoutConstraint activateConstraints:@[
[self.searchContainer.topAnchor constraintEqualToAnchor:header.bottomAnchor],
[self.searchContainer.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:8],
[self.searchContainer.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:-8],
[self.searchContainer.heightAnchor constraintEqualToConstant:80],
[searchContainer.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor constant:50],
[searchContainer.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:8],
[searchContainer.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:-8],
[searchContainer.heightAnchor constraintEqualToConstant:100],
[self.searchBar.leadingAnchor constraintEqualToAnchor:self.searchContainer.leadingAnchor],
[self.searchBar.trailingAnchor constraintEqualToAnchor:self.searchContainer.trailingAnchor constant:-8],
[self.searchBar.centerYAnchor constraintEqualToAnchor:self.searchContainer.centerYAnchor constant:-10],
[self.searchBar.leadingAnchor constraintEqualToAnchor:searchContainer.leadingAnchor],
[self.searchBar.trailingAnchor constraintEqualToAnchor:searchContainer.trailingAnchor],
[self.searchBar.centerYAnchor constraintEqualToAnchor:searchContainer.centerYAnchor constant:-10],
[self.selectAllButton.trailingAnchor constraintEqualToAnchor:self.searchContainer.trailingAnchor constant:-16],
[self.selectAllButton.trailingAnchor constraintEqualToAnchor:searchContainer.trailingAnchor constant:-16],
[self.selectAllButton.topAnchor constraintEqualToAnchor:self.searchBar.bottomAnchor constant:8],
]];
UITextField *textField = self.searchBar.searchTextField;
// Make text darker and clearer
textField.textColor = [UIColor blackColor];
textField.font = [UIFont systemFontOfSize:16 weight:UIFontWeightRegular];
// Darken placeholder
textField.attributedPlaceholder = [[NSAttributedString alloc]
initWithString:self.searchBar.placeholder
attributes:@{NSForegroundColorAttributeName: [UIColor darkGrayColor],
NSFontAttributeName: [UIFont systemFontOfSize:16]}];
// Optional: give the search field a white rounded background with shadow
textField.backgroundColor = [UIColor whiteColor];
textField.layer.cornerRadius = 10;
textField.layer.masksToBounds = YES;
textField.layer.borderColor = [UIColor colorWithWhite:0.8 alpha:1.0].CGColor; // light gray
textField.layer.borderWidth = 1.0;
}
- (void)setupTableView {
self.tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
self.tableView.translatesAutoresizingMaskIntoConstraints = NO;
self.tableView.backgroundColor = [UIColor systemGroupedBackgroundColor];
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
self.tableView.delegate = self;
self.tableView.dataSource = self;
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
self.tableView.backgroundView = nil;
self.tableView.backgroundColor = [UIColor whiteColor];
self.tableView.opaque = YES;
[self.tableView registerClass:[WithdrawIssueCell class] forCellReuseIdentifier:@"WithdrawIssueCell"];
[self.view addSubview:self.tableView];
}
- (void)setupBottomBar {
self.bottomBar = [[UIView alloc] init]; // 👈 also store in a property
self.bottomBar.backgroundColor = UIColor.whiteColor;
self.bottomBar.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:self.bottomBar];
// ... your existing counter + button setup ...
[NSLayoutConstraint activateConstraints:@[
[self.tableView.topAnchor constraintEqualToAnchor:self.searchBar.superview.bottomAnchor constant:4],
[self.tableView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:8],
[self.tableView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:-8],
[self.tableView.bottomAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.bottomAnchor constant:-60]
]];
}
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
- (void)setupBottomBar {
UIView *bottomBar = [[UIView alloc] init];
bottomBar.backgroundColor = UIColor.whiteColor;
bottomBar.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:bottomBar];
self.counterLabel = [[UILabel alloc] init];
self.counterLabel.text = @"0 / 0";
self.counterLabel.font = [UIFont systemFontOfSize:14 weight:UIFontWeightMedium];
self.counterLabel.translatesAutoresizingMaskIntoConstraints = NO;
self.counterLabel.textColor = UIColor.blackColor;
[bottomBar addSubview:self.counterLabel];
self.proceedButton = [UIButton buttonWithType:UIButtonTypeSystem];
[self.proceedButton setTitle:@"Proceed" forState:UIControlStateNormal];
self.proceedButton.backgroundColor = [UIColor colorWithRed:0.0 green:0.43 blue:0.55 alpha:1.0];
[self.proceedButton setTitleColor:UIColor.whiteColor forState:UIControlStateNormal];
self.proceedButton.layer.cornerRadius = 8;
self.proceedButton.translatesAutoresizingMaskIntoConstraints = NO;
[self.proceedButton addTarget:self action:@selector(proceedTapped) forControlEvents:UIControlEventTouchUpInside];
[bottomBar addSubview:self.proceedButton];
// Now that everything exists, activate proper constraints for the table
if (self.tableView.constraints.count == 0) {
[NSLayoutConstraint activateConstraints:@[
[self.tableView.topAnchor constraintEqualToAnchor:self.searchContainer.bottomAnchor constant:8],
[self.tableView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
[self.tableView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
[self.tableView.bottomAnchor constraintEqualToAnchor:self.bottomBar.topAnchor]
[bottomBar.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
[bottomBar.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
[bottomBar.bottomAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.bottomAnchor],
[bottomBar.heightAnchor constraintEqualToConstant:60],
[self.counterLabel.leadingAnchor constraintEqualToAnchor:bottomBar.leadingAnchor constant:16],
[self.counterLabel.centerYAnchor constraintEqualToAnchor:bottomBar.centerYAnchor],
[self.proceedButton.trailingAnchor constraintEqualToAnchor:bottomBar.trailingAnchor constant:-16],
[self.proceedButton.centerYAnchor constraintEqualToAnchor:bottomBar.centerYAnchor],
[self.proceedButton.widthAnchor constraintEqualToConstant:120],
[self.proceedButton.heightAnchor constraintEqualToConstant:40]
]];
}
}
#pragma mark - Data
......@@ -145,12 +365,8 @@
NSArray *allIssues = data[@"Data"] ?: @[];
// ✅ Filter W.I.P. only
NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(NSDictionary *issue, NSDictionary *bindings) {
NSString *status = [issue[@"status_external"] lowercaseString];
return [status isEqualToString:@"w.i.p"] || [status isEqualToString:@"wip"];
}];
// ✅ Filter to show only "W.I.P" issues
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"status_external == %@", @"W.I.P"];
self.issues = [allIssues filteredArrayUsingPredicate:predicate];
self.filteredIssues = self.issues;
......@@ -161,31 +377,61 @@
}];
}
#pragma mark - Actions
- (void)closeTapped {
- (void)handleBack {
[self dismissViewControllerAnimated:YES completion:nil];
}
- (void)selectAllTapped {
if (self.selectedIssueIDs.count == self.issues.count) {
if (self.selectedIssueIDs.count == self.filteredIssues.count) {
// All visible issues are selected, so deselect all
[self.selectedIssueIDs removeAllObjects];
} else {
for (NSDictionary *issue in self.issues) {
// Select all currently visible issues
for (NSDictionary *issue in self.filteredIssues) {
[self.selectedIssueIDs addObject:issue[@"id"]];
}
}
// Update button title dynamically
if (self.selectedIssueIDs.count == self.filteredIssues.count) {
[self.selectAllButton setTitle:@"Deselect All" forState:UIControlStateNormal];
} else {
[self.selectAllButton setTitle:@"Select All" forState:UIControlStateNormal];
}
[self.tableView reloadData];
[self updateCounter];
}
- (void)proceedTapped {
if (self.selectedIssueIDs.count == 0) {
[self showAlert:@"Please select at least one issue to proceed."];
return;
}
NSPredicate *selectedPredicate = [NSPredicate predicateWithFormat:@"id IN %@", self.selectedIssueIDs];
NSArray *filteredIssues = [self.issues filteredArrayUsingPredicate:selectedPredicate];
NSLog(@"✅ Selected issue IDs: %@", self.selectedIssueIDs);
NSLog(@"✅ Total issues available: %lu", (unsigned long)self.issues.count);
NSLog(@"✅ Filtered selected issues: %@", filteredIssues);
// Create a clean array with only the reference (and other desired fields)
NSMutableArray *formattedIssues = [NSMutableArray array];
for (NSDictionary *issue in filteredIssues) {
NSString *ref = issue[@"reference"] ?: @"";
NSString *issueId = issue[@"id"] ?: @"";
[formattedIssues addObject:@{
@"id": issueId,
@"reference": ref
}];
}
NSLog(@"✅ Final selectedIssues to send: %@", formattedIssues);
WithdrawSingleIssueViewController *vc = [[WithdrawSingleIssueViewController alloc] init];
vc.selectedIssues = [self.issues filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"id IN %@", self.selectedIssueIDs]];
vc.selectedIssues = formattedIssues; // Clean, predictable structure
vc.isMultiWithdraw = YES;
vc.modalPresentationStyle = UIModalPresentationFullScreen;
[self presentViewController:vc animated:YES completion:nil];
......@@ -201,93 +447,43 @@
[self presentViewController:alert animated:YES completion:nil];
}
#pragma mark - TableView
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.filteredIssues.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellId = @"WithdrawCardCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellId];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellId];
cell.backgroundColor = UIColor.clearColor;
cell.selectionStyle = UITableViewCellSelectionStyleNone;
// --- Card container ---
UIView *card = [[UIView alloc] initWithFrame:CGRectZero];
card.tag = 100;
card.backgroundColor = UIColor.whiteColor;
card.layer.cornerRadius = 12;
card.layer.shadowColor = [UIColor blackColor].CGColor;
card.layer.shadowOpacity = 0.1;
card.layer.shadowRadius = 4;
card.layer.shadowOffset = CGSizeMake(0, 2);
[cell.contentView addSubview:card];
card.translatesAutoresizingMaskIntoConstraints = NO;
- (void)setNoImagePlaceholder:(UIImageView *)thumbView {
// Clear any previous content
thumbView.image = nil;
thumbView.backgroundColor = [UIColor colorWithWhite:0.95 alpha:1.0];
// --- Checkmark overlay ---
UIImageView *checkIcon = [[UIImageView alloc] initWithImage:[UIImage systemImageNamed:@"checkmark.circle.fill"]];
checkIcon.tag = 202;
checkIcon.tintColor = [UIColor colorWithRed:0.0 green:0.43 blue:0.55 alpha:1.0];
checkIcon.translatesAutoresizingMaskIntoConstraints = NO;
[cell.contentView addSubview:checkIcon];
// --- Title ---
UILabel *titleLabel = [[UILabel alloc] init];
titleLabel.tag = 200;
titleLabel.font = [UIFont boldSystemFontOfSize:16];
[card addSubview:titleLabel];
titleLabel.translatesAutoresizingMaskIntoConstraints = NO;
// --- Subtitle ---
UILabel *subtitleLabel = [[UILabel alloc] init];
subtitleLabel.tag = 201;
subtitleLabel.font = [UIFont systemFontOfSize:14];
subtitleLabel.textColor = UIColor.darkGrayColor;
[card addSubview:subtitleLabel];
subtitleLabel.translatesAutoresizingMaskIntoConstraints = NO;
// --- Layout ---
[NSLayoutConstraint activateConstraints:@[
[card.leadingAnchor constraintEqualToAnchor:cell.contentView.leadingAnchor constant:16],
[card.trailingAnchor constraintEqualToAnchor:cell.contentView.trailingAnchor constant:-32], // leave space on the right for the checkmark
[card.topAnchor constraintEqualToAnchor:cell.contentView.topAnchor],
[card.bottomAnchor constraintEqualToAnchor:cell.contentView.bottomAnchor constant:-4],
[checkIcon.centerYAnchor constraintEqualToAnchor:card.centerYAnchor],
[checkIcon.leadingAnchor constraintEqualToAnchor:card.trailingAnchor constant:4], // positioned outside the card
[checkIcon.widthAnchor constraintEqualToConstant:28],
[checkIcon.heightAnchor constraintEqualToConstant:28],
[titleLabel.topAnchor constraintEqualToAnchor:card.topAnchor constant:12],
[titleLabel.leadingAnchor constraintEqualToAnchor:card.leadingAnchor constant:12],
[titleLabel.trailingAnchor constraintLessThanOrEqualToAnchor:card.trailingAnchor constant:-12],
[subtitleLabel.topAnchor constraintEqualToAnchor:titleLabel.bottomAnchor constant:4],
[subtitleLabel.leadingAnchor constraintEqualToAnchor:card.leadingAnchor constant:12],
[subtitleLabel.trailingAnchor constraintEqualToAnchor:card.trailingAnchor constant:-12],
[subtitleLabel.bottomAnchor constraintEqualToAnchor:card.bottomAnchor constant:-12]
]];
for (UIView *subview in thumbView.subviews) {
[subview removeFromSuperview];
}
// --- Configure cell ---
NSDictionary *issue = self.filteredIssues[indexPath.row];
UILabel *titleLabel = [cell.contentView viewWithTag:200];
UILabel *subtitleLabel = [cell.contentView viewWithTag:201];
UIImageView *checkIcon = [cell.contentView viewWithTag:202];
titleLabel.text = issue[@"location_name"];
subtitleLabel.text = [NSString stringWithFormat:@"%@ → %@", issue[@"category"], issue[@"issue"]];
UILabel *noImageLabel = [[UILabel alloc] init];
noImageLabel.text = @"No image";
noImageLabel.textAlignment = NSTextAlignmentCenter;
noImageLabel.font = [UIFont systemFontOfSize:10];
noImageLabel.textColor = [UIColor grayColor];
noImageLabel.translatesAutoresizingMaskIntoConstraints = NO;
[thumbView addSubview:noImageLabel];
BOOL isSelected = [self.selectedIssueIDs containsObject:issue[@"id"]];
checkIcon.hidden = !isSelected;
[NSLayoutConstraint activateConstraints:@[
[noImageLabel.centerXAnchor constraintEqualToAnchor:thumbView.centerXAnchor],
[noImageLabel.centerYAnchor constraintEqualToAnchor:thumbView.centerYAnchor]
]];
}
return cell;
- (UIColor *)colorFromHexString:(NSString *)hex {
unsigned int rgbValue = 0;
NSScanner *scanner = [NSScanner scannerWithString:hex];
if ([hex hasPrefix:@"#"]) [scanner setScanLocation:1];
[scanner scanHexInt:&rgbValue];
return [UIColor colorWithRed:((rgbValue & 0xFF0000) >> 16) / 255.0
green:((rgbValue & 0xFF00) >> 8) / 255.0
blue:(rgbValue & 0xFF) / 255.0
alpha:1.0];
}
#pragma mark - TableView
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSDictionary *issue = self.filteredIssues[indexPath.row];
NSDictionary *issue = self.filteredIssues[indexPath.section];
NSString *issueID = issue[@"id"];
if ([self.selectedIssueIDs containsObject:issueID]) {
......@@ -295,22 +491,163 @@
} else {
[self.selectedIssueIDs addObject:issueID];
}
[self updateCounter];
[tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
WithdrawIssueCell *cell = [tableView cellForRowAtIndexPath:indexPath];
BOOL isSelected = [self.selectedIssueIDs containsObject:issueID];
[cell updateSelectionState:isSelected];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return self.filteredIssues.count;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 100; // adjust to your card height
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
WithdrawIssueCell *cell = [tableView dequeueReusableCellWithIdentifier:@"WithdrawIssueCell" forIndexPath:indexPath];
// Reset reused state
cell.thumbView.image = nil;
for (UIView *subview in cell.thumbView.subviews) {
[subview removeFromSuperview];
}
NSDictionary *issue = self.filteredIssues[indexPath.section];
NSString *thumbURL = issue[@"thumb_image"] ?: @"";
// Thumbnail rendering (copied from IssueViewController)
if (thumbURL.length > 0) {
NSURL *url = [NSURL URLWithString:thumbURL];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSData *data = [NSData dataWithContentsOfURL:url];
dispatch_async(dispatch_get_main_queue(), ^{
WithdrawIssueCell *updateCell = [tableView cellForRowAtIndexPath:indexPath];
if (!updateCell) return;
// Clear any "No image" placeholder labels
for (UIView *subview in updateCell.thumbView.subviews) {
[subview removeFromSuperview];
}
if (data) {
UIImage *img = [UIImage imageWithData:data];
if (img) {
updateCell.thumbView.image = img;
updateCell.thumbView.backgroundColor = UIColor.clearColor;
} else {
[self setNoImagePlaceholder:updateCell.thumbView];
}
} else {
[self setNoImagePlaceholder:updateCell.thumbView];
}
});
});
} else {
[self setNoImagePlaceholder:cell.thumbView];
}
// Text fields
cell.referenceLabel.text = issue[@"reference"] ?: @"";
cell.statusLabel.text = issue[@"status_external"] ?: @"";
cell.statusLabel.backgroundColor = [self colorFromHexString:issue[@"status_ex_color"] ?: @""];
cell.locationLabel.text = issue[@"location_name"] ?: @"";
cell.createdLabel.text = [NSString stringWithFormat:@"Created on %@", issue[@"created_at"] ?: @""];
NSString *category = issue[@"category"] ?: @"";
NSString *type = issue[@"type"] ?: @"";
NSString *issueText = issue[@"issue"] ?: @"";
cell.detailsLabel.text = [NSString stringWithFormat:@"%@ → %@ → %@",
category, type, issueText];
// Highlight selected ones
BOOL isSelected = [self.selectedIssueIDs containsObject:issue[@"id"]];
[cell updateSelectionState:isSelected];
return cell;
}
// ✅ Add spacing between cells using header/footer height
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
return 10; // spacing between cells
return 2; // top spacing
}
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
return 2; // bottom spacing
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(nonnull NSIndexPath *)indexPath {
return 110;
}
// Remove empty header/footer lines at end
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
UIView *spacer = [[UIView alloc] init];
spacer.backgroundColor = UIColor.clearColor;
return spacer;
return [UIView new];
}
- (UIView *)tableView:(UITableView *)tableView viewForFooterInSection:(NSInteger)section {
return [UIView new];
}
#pragma mark - Search Handling
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText {
NSString *search = [searchText uppercaseString];
if (search.length == 0) {
self.filteredIssues = self.issues;
} else {
NSMutableArray *matches = [NSMutableArray array];
for (NSDictionary *issue in self.issues) {
NSString *reference = [issue[@"reference"] ?: @"" uppercaseString];
NSString *location = [issue[@"location_name"] ?: @"" uppercaseString];
NSString *remarks = [issue[@"remarks"] ?: @"" uppercaseString];
NSString *createdAt = [issue[@"created_at"] ?: @"" uppercaseString];
NSString *category = [issue[@"category"] ?: @"" uppercaseString];
NSString *type = [issue[@"type"] ?: @"" uppercaseString];
NSString *issueText = [issue[@"issue"] ?: @"" uppercaseString];
NSString *status = [issue[@"status_external"] ?: @"" uppercaseString];
if ([reference containsString:search] ||
[location containsString:search] ||
[remarks containsString:search] ||
[createdAt containsString:search] ||
[category containsString:search] ||
[type containsString:search] ||
[issueText containsString:search] ||
[status containsString:search]) {
[matches addObject:issue];
}
}
self.filteredIssues = matches;
}
[self.tableView reloadData];
}
- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
[searchBar resignFirstResponder];
}
- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
// Clear search text
searchBar.text = @"";
// Reset filtered issues
self.filteredIssues = self.issues;
[self.tableView reloadData];
// Dismiss keyboard
[searchBar resignFirstResponder];
}
- (void)searchBarTextDidBeginEditing:(UISearchBar *)searchBar {
[searchBar setShowsCancelButton:YES animated:YES];
}
- (void)searchBarTextDidEndEditing:(UISearchBar *)searchBar {
[searchBar setShowsCancelButton:NO animated:YES];
}
@end
......@@ -25,12 +25,18 @@
self.issueID, self.planID, self.status, self.unitName, self.reference, self.titleText);
[self setupHeader];
if (self.isMultiWithdraw){
}
[self setupContent];
}
#pragma mark - Header
- (void)setupHeader {
self.headerView = [[UIView alloc] init];
CGFloat statusBarHeight = UIApplication.sharedApplication.statusBarFrame.size.height;
self.headerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, statusBarHeight + 60)];
self.headerView.translatesAutoresizingMaskIntoConstraints = NO;
self.headerView.backgroundColor = [UIColor colorWithRed:0/255.0 green:109/255.0 blue:141/255.0 alpha:1];
[self.view addSubview:self.headerView];
......@@ -80,16 +86,32 @@
promptLabel.translatesAutoresizingMaskIntoConstraints = NO;
promptLabel.text = @"Write your message:";
promptLabel.font = [UIFont systemFontOfSize:16 weight:UIFontWeightMedium];
promptLabel.textColor = [UIColor darkGrayColor];
[self.view addSubview:promptLabel];
self.messageInput = [[UITextView alloc] init];
self.messageInput.translatesAutoresizingMaskIntoConstraints = NO;
self.messageInput.font = [UIFont systemFontOfSize:16];
// --- Styling ---
self.messageInput.backgroundColor = [UIColor colorWithWhite:0.97 alpha:1.0]; // soft light gray
self.messageInput.textColor = [UIColor blackColor];
self.messageInput.layer.borderWidth = 1.0;
self.messageInput.layer.borderColor = [UIColor lightGrayColor].CGColor;
self.messageInput.layer.borderColor = [UIColor colorWithWhite:0.85 alpha:1.0].CGColor;
self.messageInput.layer.cornerRadius = 8;
self.messageInput.layer.masksToBounds = YES;
[self.view addSubview:self.messageInput];
// --- CREATE confirmButton BEFORE using it in constraints ---
self.confirmButton = [UIButton buttonWithType:UIButtonTypeSystem];
self.confirmButton.translatesAutoresizingMaskIntoConstraints = NO;
[self.confirmButton setTitle:@"Submit" forState:UIControlStateNormal];
self.confirmButton.backgroundColor = [UIColor colorWithRed:0/255.0 green:109/255.0 blue:141/255.0 alpha:1];
[self.confirmButton setTitleColor:UIColor.whiteColor forState:UIControlStateNormal];
self.confirmButton.layer.cornerRadius = 8;
[self.confirmButton addTarget:self action:@selector(handleConfirm) forControlEvents:UIControlEventTouchUpInside];
// don't addSubview yet if you prefer, we'll add below before constraints
UIView *separator = [[UIView alloc] init];
separator.translatesAutoresizingMaskIntoConstraints = NO;
separator.backgroundColor = [UIColor colorWithWhite:0.8 alpha:1];
......@@ -114,24 +136,46 @@
// ✅ If multiple issues selected — show a list below comment box
if (self.isMultiWithdraw) {
UILabel *listLabel = [[UILabel alloc] init];
listLabel.translatesAutoresizingMaskIntoConstraints = NO;
listLabel.text = @"Selected Issues";
listLabel.font = [UIFont systemFontOfSize:16 weight:UIFontWeightSemibold];
[self.view addSubview:listLabel];
// "Withdrawing" title
UILabel *withdrawingLabel = [[UILabel alloc] init];
withdrawingLabel.translatesAutoresizingMaskIntoConstraints = NO;
withdrawingLabel.text = @"Withdrawing";
withdrawingLabel.font = [UIFont boldSystemFontOfSize:16];
withdrawingLabel.textColor = [UIColor blackColor]; // main title dark
withdrawingLabel.backgroundColor = [UIColor clearColor];
[self.view addSubview:withdrawingLabel];
// "X Issue(s)" label
NSString *countText = [NSString stringWithFormat:@"%lu Issue(s)", (unsigned long)self.selectedIssues.count];
UILabel *countLabel = [[UILabel alloc] init];
countLabel.translatesAutoresizingMaskIntoConstraints = NO;
countLabel.text = countText;
countLabel.font = [UIFont systemFontOfSize:14];
countLabel.textColor = [UIColor colorWithWhite:0.4 alpha:1.0]; // subtle gray text
[self.view addSubview:countLabel];
// Table showing list of issues
self.issuesTable = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStyleInsetGrouped];
self.issuesTable.translatesAutoresizingMaskIntoConstraints = NO;
self.issuesTable.dataSource = self;
self.issuesTable.delegate = self;
self.issuesTable.layer.cornerRadius = 8;
self.issuesTable.layer.masksToBounds = YES;
self.issuesTable.layer.borderColor = [UIColor colorWithWhite:0.9 alpha:1.0].CGColor;
self.issuesTable.layer.borderWidth = 1.0;
[self.issuesTable registerClass:[UITableViewCell class] forCellReuseIdentifier:@"WithdrawListCell"];
[self.view addSubview:self.issuesTable];
[constraints addObjectsFromArray:@[
[listLabel.topAnchor constraintEqualToAnchor:separator.bottomAnchor constant:20],
[listLabel.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:20],
[self.issuesTable.topAnchor constraintEqualToAnchor:listLabel.bottomAnchor constant:8],
[self.issuesTable.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
[self.issuesTable.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
[withdrawingLabel.topAnchor constraintEqualToAnchor:separator.bottomAnchor constant:20],
[withdrawingLabel.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:20],
[countLabel.topAnchor constraintEqualToAnchor:withdrawingLabel.bottomAnchor constant:4],
[countLabel.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:20],
[self.issuesTable.topAnchor constraintEqualToAnchor:countLabel.bottomAnchor constant:8],
[self.issuesTable.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:20],
[self.issuesTable.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:-20],
[self.issuesTable.heightAnchor constraintEqualToConstant:200],
]];
......@@ -146,7 +190,9 @@
]];
}
// Add confirm button to view so its anchors are valid
[self.view addSubview:self.confirmButton];
[constraints addObjectsFromArray:@[
[self.confirmButton.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:40],
[self.confirmButton.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:-40],
......@@ -157,7 +203,6 @@
[NSLayoutConstraint activateConstraints:constraints];
}
#pragma mark - Actions
- (void)handleBack {
[self dismissViewControllerAnimated:YES completion:nil];
......@@ -169,42 +214,48 @@
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Alert"
message:@"Please enter a message before submitting."
preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"OK"
style:UIAlertActionStyleDefault
handler:nil]];
[alert addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]];
[self presentViewController:alert animated:YES completion:nil];
return;
}
// Confirmation dialog before sending
UIAlertController *confirm = [UIAlertController alertControllerWithTitle:@"Confirm"
message:@"Are you sure you want to submit?"
preferredStyle:UIAlertControllerStyleAlert];
[confirm addAction:[UIAlertAction actionWithTitle:@"No" style:UIAlertActionStyleCancel handler:nil]];
[confirm addAction:[UIAlertAction actionWithTitle:@"Yes" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
if (self.isMultiWithdraw) {
// Multi-withdraw
// Extract only IDs (and skip nils)
NSMutableArray *ids = [NSMutableArray array];
for (NSDictionary *issue in self.selectedIssues) {
if (issue[@"id"]) [ids addObject:issue[@"id"]];
for (NSDictionary *issue in self.selectedIssues ?: @[]) {
id issueId = issue[@"id"];
if (issueId) [ids addObject:issueId];
}
[IssueAPIClient fetchVoidIssue:ids
remarks:message
completion:^(NSDictionary *data, NSError *error) {
[self handleVoidIssueResponse:data error:error];
}];
NSLog(@"📦 Extracted IDs for API: %@", ids);
if (ids.count == 0) {
[self showAlertWithTitle:@"Alert" message:@"No valid issue IDs to submit."];
return;
}
[self requestVoidIssue:ids remarks:message];
}
else if ([self.titleText containsString:@"Delete"]) {
if (self.issueID == nil) {
[self showAlertWithTitle:@"Alert" message:@"No issue selected to delete."];
return;
}
[self requestDeleteIssue:self.issueID remarks:message];
}
else {
NSArray *singleIssueArray = @[ self.issueID ?: @"" ];
[IssueAPIClient fetchVoidIssue:singleIssueArray
remarks:message
completion:^(NSDictionary *data, NSError *error) {
[self handleVoidIssueResponse:data error:error];
}];
if (self.issueID == nil) {
[self showAlertWithTitle:@"Alert" message:@"No issue selected to void."];
return;
}
NSArray *singleIssueArray = @[ self.issueID ];
[self requestVoidIssue:singleIssueArray remarks:message];
}
}]];
[self presentViewController:confirm animated:YES completion:nil];
......@@ -247,33 +298,28 @@
}];
}
# pragma mark - helper
- (void)showAlertWithTitle:(NSString *)title message:(NSString *)message {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:title
message:message
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *ok = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil];
[alert addAction:ok];
[self presentViewController:alert animated:YES completion:nil];
}
-(void)requestVoidIssue:(NSArray *)ids remarks:(NSString *)remarks {
NSLog(@"🚀 Sending VOID ISSUE for ID=%@, remarks=%@", self.selectedIssues, remarks);
- (void)handleVoidIssueResponse:(NSDictionary *)data error:(NSError *)error {
[IssueAPIClient fetchVoidIssue:ids
remarks:remarks
completion:^(NSDictionary *data, NSError *error) {
if (error) {
NSLog(@"❌ Void Issue Error: %@", error.localizedDescription);
[self showAlertWithTitle:@"Error" message:error.localizedDescription];
NSLog(@"Error %@", error.localizedDescription);
return;
}
NSDictionary *appData = data[@"AppData"];
if ([appData[@"status"] isEqualToString:@"success"]) {
NSLog(@"✅ Issues voided successfully!");
NSLog(@"✅ Issue voided successfully!");
UIAlertController *successAlert = [UIAlertController alertControllerWithTitle:@"Success"
message:@"Issues withdrawn successfully."
message:@"Issue voided successfully."
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *ok = [UIAlertAction actionWithTitle:@"OK"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * _Nonnull action) {
// 🏠 Return to dashboard or issue list
// 🏠 Mimic AddIssue success dismissal logic
UIViewController *presenter = self.presentingViewController;
if (presenter.presentingViewController) {
[presenter.presentingViewController dismissViewControllerAnimated:YES completion:nil];
......@@ -284,15 +330,27 @@
[successAlert addAction:ok];
[self presentViewController:successAlert animated:YES completion:nil];
} else {
NSString *message = appData[@"message"] ?: @"Unknown error occurred.";
NSLog(@"⚠️ Void Issue failed: %@", message);
[self showAlertWithTitle:@"Failed" message:message];
NSString *message = appData[@"message"] ?: @"Unknown error";
NSLog(@"⚠️ Error Message %@", message);
[self showAlertWithTitle:@"Void Failed" message:message];
}
}];
}
# pragma mark - helper
- (void)showAlertWithTitle:(NSString *)title message:(NSString *)message {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:title
message:message
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *ok = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil];
[alert addAction:ok];
[self presentViewController:alert animated:YES completion:nil];
}
#pragma mark - UITableView
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.selectedIssues.count;
return (NSInteger)(self.selectedIssues ? self.selectedIssues.count : 0);
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
......@@ -302,16 +360,38 @@
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellId];
}
if (indexPath.row < self.selectedIssues.count) {
NSDictionary *issue = self.selectedIssues[indexPath.row];
cell.textLabel.text = issue[@"reference"] ?: @"(No Ref)";
NSString *loc = issue[@"location_name"] ?: @"";
NSString *cat = issue[@"category"] ?: @"";
NSString *typ = issue[@"type"] ?: @"";
cell.detailTextLabel.text = [NSString stringWithFormat:@"%@ → %@ → %@", loc, cat, typ];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
} else {
cell.textLabel.text = @"(No Ref)";
cell.detailTextLabel.text = @"";
}
cell.backgroundColor = [UIColor whiteColor];
cell.textLabel.textColor = [UIColor blackColor];
cell.detailTextLabel.textColor = [UIColor colorWithWhite:0.4 alpha:1.0];
cell.contentView.backgroundColor = [UIColor colorWithWhite:0.98 alpha:1.0];
cell.layer.cornerRadius = 8;
cell.layer.masksToBounds = NO;
cell.layer.shadowColor = [UIColor colorWithWhite:0 alpha:0.1].CGColor;
cell.layer.shadowOffset = CGSizeMake(0, 2);
cell.layer.shadowOpacity = 0.3;
cell.layer.shadowRadius = 4;
cell.layer.borderWidth = 0.5;
cell.layer.borderColor = [UIColor colorWithWhite:0.9 alpha:1.0].CGColor;
cell.textLabel.font = [UIFont boldSystemFontOfSize:15];
cell.detailTextLabel.font = [UIFont systemFontOfSize:13];
cell.textLabel.textColor = [UIColor colorWithRed:0.0 green:0.43 blue:0.55 alpha:1.0];
cell.detailTextLabel.textColor = [UIColor darkGrayColor];
cell.selectionStyle = UITableViewCellSelectionStyleNone;
return cell;
}
@end
......@@ -6,7 +6,7 @@
<dict>
<key>Assets.car</key>
<data>
KnMHH8GU4NZESWbs6/0uU5WX8uA=
EqtjfGVJUrKupCnFW6lfffSo2ZA=
</data>
<key>Headers/QmsPlugin.h</key>
<data>
......@@ -18,7 +18,7 @@
</data>
<key>Info.plist</key>
<data>
ScoY0XAgmZ3U9Q0BvlfgQfJm2bo=
w8N5kkR8hr/wIwb/x0XA5+wVpdo=
</data>
<key>Modules/module.modulemap</key>
<data>
......@@ -31,7 +31,7 @@
<dict>
<key>hash2</key>
<data>
aLAhYopvL1Lb4CTMG5A8x8RAmnkpXwHQbjsJdpq8gYg=
k78SvGog/XWiM8sEWRQ8ZyFHnocCgVMjS4SisBwChjI=
</data>
</dict>
<key>Headers/QmsPlugin.h</key>
......
......@@ -6,7 +6,7 @@
<dict>
<key>Assets.car</key>
<data>
KnMHH8GU4NZESWbs6/0uU5WX8uA=
EqtjfGVJUrKupCnFW6lfffSo2ZA=
</data>
<key>Headers/QmsPlugin.h</key>
<data>
......@@ -18,7 +18,7 @@
</data>
<key>Info.plist</key>
<data>
JGviPenfphujtQEGyJZCIZTsd2c=
uEkE+l7bQzwu7vnX83uBlOCEpCk=
</data>
<key>Modules/module.modulemap</key>
<data>
......@@ -31,7 +31,7 @@
<dict>
<key>hash2</key>
<data>
aLAhYopvL1Lb4CTMG5A8x8RAmnkpXwHQbjsJdpq8gYg=
k78SvGog/XWiM8sEWRQ8ZyFHnocCgVMjS4SisBwChjI=
</data>
</dict>
<key>Headers/QmsPlugin.h</key>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment