Commit 61a8bfd5 authored by Wei Han's avatar Wei Han

code update

parent 5ac78689
......@@ -275,112 +275,111 @@
images:(NSArray<UIImage *> * _Nullable)images
completion:(void (^)(NSDictionary * _Nullable response, NSError * _Nullable error))completion {
NSString *urlString = @"https://kitadev.commudesk.com/api/owner/issue/addIssue?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6ImF0aGlyYWh6YWlkaUBjb252ZXAuY29tIiwicGFzc3dvcmQiOiIkMnkkMTAkNU9xYklqMnZ6UHJwckJrcVd5c3I2LmJCc1hVYS9Hd014eUhnL3RhUUhPUkNQcGdmTG8yakciLCJzdWIiOjIxNzAsImlzcyI6Imh0dHBzOi8va2l0YWRldi5jb21tdWRlc2suY29tL2FwaS9vd25lci9hdXRoL3Bhc3N3b3JkbGVzc19sb2dpbiIsImlhdCI6MTc2MDMxNTM1MiwiZXhwIjoyMDc1ODg0ODcyLCJuYmYiOjE3NjAzMTUzNTIsImp0aSI6IktoQmNyQnJEdDVNdDZlWmMifQ.JnxGbZ7hTeoOr6oibIzZMphgyKtTo2Aq9Efx6mrGfFA"; NSURL *url = [NSURL URLWithString:urlString];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST";
// 🧱 Multipart body setup
NSString *boundary = [NSString stringWithFormat:@"Boundary-%@", [[NSUUID UUID] UUIDString]];
[request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary]
forHTTPHeaderField:@"Content-Type"];
NSMutableData *body = [NSMutableData data];
// Helper for form fields
void (^appendFormField)(NSString *, NSString *) = ^(NSString *key, NSString *value) {
[body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary]
dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:
@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", key]
dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:@"%@\r\n", value]
dataUsingEncoding:NSUTF8StringEncoding]];
};
// 🔹 Append update fields (all under data[])
for (NSString *key in updateData.allKeys) {
id value = updateData[key];
if ([value isKindOfClass:[NSNumber class]]) value = [(NSNumber *)value stringValue];
if ([value isKindOfClass:[NSString class]]) {
appendFormField([NSString stringWithFormat:@"data[%@]", key], value);
}
NSString *urlString = @"https://kitadev.commudesk.com/api/owner/issue/updateIssue?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6ImF0aGlyYWh6YWlkaUBjb252ZXAuY29tIiwicGFzc3dvcmQiOiIkMnkkMTAkNU9xYklqMnZ6UHJwckJrcVd5c3I2LmJCc1hVYS9Hd014eUhnL3RhUUhPUkNQcGdmTG8yakciLCJzdWIiOjIxNzAsImlzcyI6Imh0dHBzOi8va2l0YWRldi5jb21tdWRlc2suY29tL2FwaS9vd25lci9hdXRoL3Bhc3N3b3JkbGVzc19sb2dpbiIsImlhdCI6MTc2MDMxNTM1MiwiZXhwIjoyMDc1ODg0ODcyLCJuYmYiOjE3NjAzMTUzNTIsImp0aSI6IktoQmNyQnJEdDVNdDZlWmMifQ.JnxGbZ7hTeoOr6oibIzZMphgyKtTo2Aq9Efx6mrGfFA"; NSURL *url = [NSURL URLWithString:urlString];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST";
// 🧱 Multipart body setup
NSString *boundary = [NSString stringWithFormat:@"Boundary-%@", [[NSUUID UUID] UUIDString]];
[request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary]
forHTTPHeaderField:@"Content-Type"];
NSMutableData *body = [NSMutableData data];
// Helper for form fields
void (^appendFormField)(NSString *, NSString *) = ^(NSString *key, NSString *value) {
[body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary]
dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:
@"Content-Disposition: form-data; name=\"%@\"\r\n\r\n", key]
dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:@"%@\r\n", value]
dataUsingEncoding:NSUTF8StringEncoding]];
};
// 🔹 Append update fields (all under data[])
for (NSString *key in updateData.allKeys) {
id value = updateData[key];
if ([value isKindOfClass:[NSNumber class]]) value = [(NSNumber *)value stringValue];
if ([value isKindOfClass:[NSString class]]) {
appendFormField([NSString stringWithFormat:@"data[%@]", key], value);
}
}
// 🔹 Always include OS
appendFormField(@"data[plan_id]", @"135");
appendFormField(@"data[location_id]", updateData[@"location_id"]);
appendFormField(@"data[issue_id]", updateData[@"issue_id"]);
appendFormField(@"data[issue_setting_id]", updateData[@"issue_setting_id"]);
appendFormField(@"data[position_x]", updateData[@"position_x"]);
appendFormField(@"data[position_y]", updateData[@"position_y"]);
appendFormField(@"data[comment]", updateData[@"comment"]);
appendFormField(@"data[os]", updateData[@"os"]);
// 🔹 Add `information` field (as JSON string)
// NSString *deviceInfo = @"{\"OS\":\"AND\",\"IMEI\":\"AND:2a2f13ff17b1cbb5\",\"OS_VERSION\":\"35\",\"MODEL\":\"2306EPN60G\",\"APP_VERSION\":\"1.22.26\"}";
// appendFormField(@"information", deviceInfo);
// 🔹 Append image files
for (int i = 0; i < images.count; i++) {
id maybeImage = images[i];
if (![maybeImage isKindOfClass:[UIImage class]]) {
NSLog(@"⚠️ Skipping non-UIImage at index %d: %@", i, maybeImage);
continue; // skip invalid entries
}
// 🔹 Always include OS
appendFormField(@"data[os]", @"AND");
UIImage *image = (UIImage *)maybeImage;
NSData *imageData = UIImageJPEGRepresentation(image, 0.8);
if (!imageData) continue;
// 🔹 Add `information` field (as JSON string)
NSDictionary *info = @{
@"os": @"AND",
@"drawing_plan_id": updateData[@"plan_id"] ?: @"",
@"information": @{
@"OS": @"AND",
@"IMEI": @"AND:2a2f13ff17b1cbb5",
@"OS_VERSION": @"35",
@"MODEL": @"2306EPN60G",
@"APP_VERSION": @"1.22.26"
}
};
NSError *jsonError;
NSData *infoData = [NSJSONSerialization dataWithJSONObject:info options:0 error:&jsonError];
if (!jsonError) {
NSString *infoString = [[NSString alloc] initWithData:infoData encoding:NSUTF8StringEncoding];
appendFormField(@"data[information]", infoString);
}
// 🔹 Append image files
for (int i = 0; i < images.count; i++) {
UIImage *image = images[i];
NSData *imageData = UIImageJPEGRepresentation(image, 0.8);
if (!imageData) continue;
[body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary]
dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:
@"Content-Disposition: form-data; name=\"data[image][%d]\"; filename=\"image%d.jpg\"\r\n", i, i]
dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[@"Content-Type: image/jpeg\r\n\r\n"
dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:imageData];
[body appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
}
[body appendData:[[NSString stringWithFormat:@"--%@--\r\n", boundary]
[body appendData:[[NSString stringWithFormat:@"--%@\r\n", boundary]
dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:[[NSString stringWithFormat:
@"Content-Disposition: form-data; name=\"data[image][%d]\"; filename=\"image%d.jpg\"\r\n", i, i]
dataUsingEncoding:NSUTF8StringEncoding]];
request.HTTPBody = body;
[body appendData:[@"Content-Type: image/jpeg\r\n\r\n"
dataUsingEncoding:NSUTF8StringEncoding]];
[body appendData:imageData];
[body appendData:[@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]];
}
[body appendData:[[NSString stringWithFormat:@"--%@--\r\n", boundary]
dataUsingEncoding:NSUTF8StringEncoding]];
request.HTTPBody = body;
NSLog(@"📦 [UpdateIssue] Uploading %lu image(s)", (unsigned long)images.count);
// 🔹 Execute request
NSURLSessionDataTask *task = [[NSURLSession sharedSession]
dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
NSLog(@"📦 [UpdateIssue] Uploading %lu image(s)", (unsigned long)images.count);
if (error) {
NSLog(@"❌ [UpdateIssue] Network error: %@", error);
dispatch_async(dispatch_get_main_queue(), ^{
if (completion) completion(nil, error);
});
return;
}
// 🔹 Execute request
NSURLSessionDataTask *task = [[NSURLSession sharedSession]
dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (error) {
NSLog(@"❌ [UpdateIssue] Network error: %@", error);
dispatch_async(dispatch_get_main_queue(), ^{
if (completion) completion(nil, error);
});
return;
}
NSError *jsonErr;
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonErr];
if (jsonErr) {
NSString *raw = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"⚠️ [UpdateIssue] JSON parse error: %@\nRaw: %@", jsonErr, raw);
dispatch_async(dispatch_get_main_queue(), ^{
if (completion) completion(nil, jsonErr);
});
return;
}
NSLog(@"✅ [UpdateIssue] Response: %@", json);
NSError *jsonErr;
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonErr];
if (jsonErr) {
NSString *raw = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"⚠️ [UpdateIssue] JSON parse error: %@\nRaw: %@", jsonErr, raw);
dispatch_async(dispatch_get_main_queue(), ^{
if (completion) completion(json, nil);
if (completion) completion(nil, jsonErr);
});
}];
[task resume];
}
return;
}
NSLog(@"✅ [UpdateIssue] Response: %@", json);
dispatch_async(dispatch_get_main_queue(), ^{
if (completion) completion(json, nil);
});
}];
[task resume];
}
@end
......@@ -18,7 +18,8 @@
@property (nonatomic, copy) NSString *selectedTypeName;
@property (nonatomic, copy) NSString * selectedIssueID;
@property (nonatomic, assign) BOOL isEditMode;
@property (nonatomic, strong) NSMutableArray *existingImages;
@property (nonatomic, strong) NSMutableArray *addedImages;
@end
@implementation AddIssueDetailsViewController
......@@ -26,6 +27,8 @@
- (void)viewDidLoad {
[super viewDidLoad];
self.isEditMode = [self.action isEqualToString:@"edit_issue"];
self.existingImages = [NSMutableArray array];
self.addedImages = [NSMutableArray array];
self.view.backgroundColor = UIColor.whiteColor;
NSLog(@"📦 Data being brought over:\n planID: %@\n locationID: %@\n selectedLocationName: %@\n defectMatrix: %@\n projectName: %@\n projectCode: %@\n XCoordinates: %f\n YCoordinates: %f\n action: %@\n issueFullData: %@\n issueContent: %@\n locationData: %@",
......@@ -148,7 +151,8 @@
NSDictionary *imgDict = firstImages[i];
NSString *urlStr = imgDict[@"thumb_image"] ?: imgDict[@"image"];
if (![urlStr isKindOfClass:[NSString class]]) continue;
[self.existingImages addObject:urlStr];
NSURL *url = [NSURL URLWithString:urlStr];
if (!url) continue;
......@@ -228,6 +232,7 @@
title.textColor = UIColor.whiteColor;
title.font = [UIFont systemFontOfSize:18 weight:UIFontWeightMedium];
title.textAlignment = NSTextAlignmentCenter;
title.tag = 101;
[self.headerView addSubview:title];
// ✅ Reset button (refresh icon)
......@@ -733,10 +738,16 @@ didFinishPickingMediaWithInfo:(NSDictionary<UIImagePickerControllerInfoKey,id> *
NSInteger index = picker.view.tag; // will be 0–2 after fix
// Store image
if (index < self.uploadedImages.count) {
self.uploadedImages[index] = image;
if (self.isEditMode) {
// Only new images go here
[self.addedImages addObject:image];
} else {
[self.uploadedImages addObject:image];
// Normal add mode (keep using uploadedImages)
if (index < self.uploadedImages.count) {
self.uploadedImages[index] = image;
} else {
[self.uploadedImages addObject:image];
}
}
// Update UI
......@@ -890,19 +901,10 @@ didFinishPickingMediaWithInfo:(NSDictionary<UIImagePickerControllerInfoKey,id> *
NSLog(@"⚠️ Edit mode is ON, but no issueFullData passed!");
}
// Prefer issue_id first, fallback to id
NSString *issueID = nil;
if ([self.issueFullData[@"issue_id"] isKindOfClass:[NSString class]]) {
issueID = self.issueFullData[@"issue_id"];
} else if ([self.issueFullData[@"id"] isKindOfClass:[NSString class]]) {
issueID = self.issueFullData[@"id"];
} else {
issueID = [NSString stringWithFormat:@"%@", self.issueFullData[@"issue_id"] ?: self.issueFullData[@"id"] ?: @"0"];
}
// ✅ Build update payload
NSDictionary *updateData = @{
@"issue_id": issueID ?: @"0",
@"location_id": self.locationID ?: @"",
@"issue_id": self.issueFullData[@"issue_reference_id"] ?: @"0",
@"issue_setting_id": self.selectedIssueID ?: @"",
@"position_x": @(self.planX).stringValue ?: @"",
@"position_y": @(self.planY).stringValue ?: @"",
......@@ -913,7 +915,7 @@ didFinishPickingMediaWithInfo:(NSDictionary<UIImagePickerControllerInfoKey,id> *
NSLog(@"📦 Sending UpdateIssue with data: %@", updateData);
[AddIssueAPIClient submitUpdateIssue:updateData
images:self.uploadedImages
images:self.addedImages
completion:^(NSDictionary * _Nullable response, NSError * _Nullable error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (error) {
......@@ -952,7 +954,8 @@ didFinishPickingMediaWithInfo:(NSDictionary<UIImagePickerControllerInfoKey,id> *
});
}];
} else {
NSLog(@"Calling addIssue API:\n planID: %@\n locationID: %@\n selectedIssueID: %@\n XCoordinates: %f\n YCoordinates: %f\n commentField: %@",
NSLog(@"Calling addIssue API:\n locationID: %@\n planID: %@\n locationID: %@\n selectedIssueID: %@\n XCoordinates: %f\n YCoordinates: %f\n commentField: %@",
self.locationID,
self.planID,
self.locationID,
self.selectedIssueID,
......
......@@ -55,6 +55,13 @@
[self refreshDashboardContent];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
NSLog(@"Dashboard loaded, refreshing data");
[self refreshDashboardContent];
}
#pragma mark - Refresh Logic
- (void)refreshDashboardContent {
NSLog(@"🔁 Refreshing dashboard content...");
......
......@@ -414,7 +414,7 @@
HistoryViewController *nextVC = [[HistoryViewController alloc] init];
nextVC.modalPresentationStyle = UIModalPresentationFullScreen;
nextVC.issueID = self.issueData[@"issue_reference_id"] ?: self.issueData[@"id"] ?: @"";
nextVC.issueID = self.issueData[@"issue_id"] ?: @"";
nextVC.planID = self.issueData[@"plan_id"];
nextVC.status = self.issueData[@"status_external"];
nextVC.unitName = self.issueData[@"plan_unit"];
......@@ -460,7 +460,7 @@
} mutableCopy];
if (data[@"id"] && ![data[@"id"] isKindOfClass:[NSNull class]]) {
issueContent[@"issue_id"] = [NSString stringWithFormat:@"%@", data[@"id"]];
issueContent[@"issue_id"] = [NSString stringWithFormat:@"%@", data[@"issue_reference_id"]];
}
if (data[@"pos_x"] && ![data[@"pos_x"] isKindOfClass:[NSNull class]]) {
issueContent[@"pos_x"] = [NSString stringWithFormat:@"%@", data[@"pos_x"]];
......@@ -522,6 +522,7 @@
withdrawVC.unitName = unitName;
withdrawVC.reference = reference;
withdrawVC.titleText = strTitle;
withdrawVC.isMultiWithdraw = NO;
NSLog(@"📦 Navigating to WithdrawSingleIssueViewController with:\n issueID=%@\n planID=%@\n status=%@\n unitName=%@\n reference=%@\n title=%@",
issueID, planID, status, unitName, reference, strTitle);
......
......@@ -3,6 +3,7 @@
#import "IssueAPIClient.h"
#import "IssueDetailViewController.h"
#import "WithdrawSingleIssueViewController.h"
#import "WithdrawIssuesViewController.h"
#pragma mark - Internal IssueCell
@interface IssueCell : UITableViewCell
......@@ -118,7 +119,6 @@
@property (nonatomic, strong) UIButton *backButton;
@property (nonatomic, strong) UILabel *titleLabel;
@property (nonatomic, strong) UIButton *withdrawButton;
@property (nonatomic, strong) UIButton *poaButton;
@property (nonatomic, strong) UIButton *filterButton;
@property (nonatomic, strong) UILabel *filterLabel;
@property (nonatomic, strong) UISearchBar *searchBar;
......@@ -181,14 +181,6 @@
self.withdrawButton.translatesAutoresizingMaskIntoConstraints = NO;
[self.withdrawButton addTarget:self action:@selector(withdrawTapped) forControlEvents:UIControlEventTouchUpInside];
[self.headerView addSubview:self.withdrawButton];
// POA Button
self.poaButton = [UIButton buttonWithType:UIButtonTypeSystem];
[self.poaButton setImage:[UIImage systemImageNamed:@"doc.text"] forState:UIControlStateNormal];
self.poaButton.tintColor = UIColor.whiteColor;
self.poaButton.translatesAutoresizingMaskIntoConstraints = NO;
[self.poaButton addTarget:self action:@selector(poaTapped) forControlEvents:UIControlEventTouchUpInside];
[self.headerView addSubview:self.poaButton];
}
- (void)setupFilterBar {
......@@ -247,10 +239,8 @@
[self.backButton.centerYAnchor constraintEqualToAnchor:self.headerView.centerYAnchor],
[self.titleLabel.centerXAnchor constraintEqualToAnchor:self.headerView.centerXAnchor],
[self.titleLabel.centerYAnchor constraintEqualToAnchor:self.headerView.centerYAnchor],
[self.withdrawButton.trailingAnchor constraintEqualToAnchor:self.headerView.trailingAnchor constant:-56],
[self.withdrawButton.trailingAnchor constraintEqualToAnchor:self.headerView.trailingAnchor constant:-12],
[self.withdrawButton.centerYAnchor constraintEqualToAnchor:self.headerView.centerYAnchor],
[self.poaButton.trailingAnchor constraintEqualToAnchor:self.headerView.trailingAnchor constant:-12],
[self.poaButton.centerYAnchor constraintEqualToAnchor:self.headerView.centerYAnchor],
// Filter
[self.filterButton.topAnchor constraintEqualToAnchor:self.headerView.bottomAnchor constant:12],
......@@ -402,10 +392,10 @@
- (void)withdrawTapped {
NSLog(@"Withdraw button tapped");
}
- (void)poaTapped {
NSLog(@"POA button tapped");
WithdrawIssuesViewController *withdrawVC = [[WithdrawIssuesViewController alloc] init];
withdrawVC.modalPresentationStyle = UIModalPresentationFullScreen;
[self presentViewController:withdrawVC animated:YES completion:nil];
}
# pragma mark - Filter
......
// WithdrawIssuesViewController.h
#import <UIKit/UIKit.h>
@interface WithdrawIssuesViewController : UIViewController
@property (nonatomic, strong) NSString *unitID; // passed from previous screen if needed
@end
// WithdrawIssuesViewController.mm
#import "WithdrawIssuesViewController.h"
#import "IssueAPIClient.h"
#import "WithdrawSingleIssueViewController.h"
@interface WithdrawIssuesViewController () <UITableViewDelegate, UITableViewDataSource, UISearchBarDelegate>
@property (nonatomic, strong) UIButton *closeButton;
@property (nonatomic, strong) UILabel *titleLabel;
@property (nonatomic, strong) UISearchBar *searchBar;
@property (nonatomic, strong) UIButton *filterButton;
@property (nonatomic, strong) UIButton *selectAllButton;
@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;
@end
@implementation WithdrawIssuesViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = UIColor.systemGroupedBackgroundColor;
self.selectedIssueIDs = [NSMutableSet set];
[self setupHeader];
[self setupSearchBar];
[self setupTableView];
[self setupBottomBar];
[self fetchIssues];
}
#pragma mark - UI Setup
- (void)setupHeader {
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];
self.titleLabel = [[UILabel alloc] init];
self.titleLabel.text = @"Withdraw Issue";
self.titleLabel.textColor = UIColor.whiteColor;
self.titleLabel.font = [UIFont boldSystemFontOfSize:18];
self.titleLabel.translatesAutoresizingMaskIntoConstraints = NO;
[header addSubview:self.titleLabel];
[NSLayoutConstraint activateConstraints:@[
[header.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor],
[header.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
[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.titleLabel.centerXAnchor constraintEqualToAnchor:header.centerXAnchor],
[self.titleLabel.centerYAnchor constraintEqualToAnchor:header.centerYAnchor],
]];
}
- (void)setupSearchBar {
self.searchContainer = [[UIView alloc] init]; // 👈 keep a property reference
self.searchContainer.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:self.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.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;
[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],
[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.selectAllButton.trailingAnchor constraintEqualToAnchor:self.searchContainer.trailingAnchor constant:-16],
[self.selectAllButton.topAnchor constraintEqualToAnchor:self.searchBar.bottomAnchor constant:8],
]];
}
- (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.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 ...
}
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
// 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]
]];
}
}
#pragma mark - Data
- (void)fetchIssues {
[IssueAPIClient fetchIssues:^(NSDictionary *data, NSError *error) {
if (error) return;
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"];
}];
self.issues = [allIssues filteredArrayUsingPredicate:predicate];
self.filteredIssues = self.issues;
dispatch_async(dispatch_get_main_queue(), ^{
self.counterLabel.text = [NSString stringWithFormat:@"0 / %lu", (unsigned long)self.issues.count];
[self.tableView reloadData];
});
}];
}
#pragma mark - Actions
- (void)closeTapped {
[self dismissViewControllerAnimated:YES completion:nil];
}
- (void)selectAllTapped {
if (self.selectedIssueIDs.count == self.issues.count) {
[self.selectedIssueIDs removeAllObjects];
} else {
for (NSDictionary *issue in self.issues) {
[self.selectedIssueIDs addObject:issue[@"id"]];
}
}
[self.tableView reloadData];
[self updateCounter];
}
- (void)proceedTapped {
if (self.selectedIssueIDs.count == 0) {
[self showAlert:@"Please select at least one issue to proceed."];
return;
}
WithdrawSingleIssueViewController *vc = [[WithdrawSingleIssueViewController alloc] init];
vc.selectedIssues = [self.issues filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"id IN %@", self.selectedIssueIDs]];
vc.isMultiWithdraw = YES;
vc.modalPresentationStyle = UIModalPresentationFullScreen;
[self presentViewController:vc animated:YES completion:nil];
}
- (void)updateCounter {
self.counterLabel.text = [NSString stringWithFormat:@"%lu / %lu", (unsigned long)self.selectedIssueIDs.count, (unsigned long)self.issues.count];
}
- (void)showAlert:(NSString *)message {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Alert" message:message preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]];
[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;
// --- 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]
]];
}
// --- 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"]];
BOOL isSelected = [self.selectedIssueIDs containsObject:issue[@"id"]];
checkIcon.hidden = !isSelected;
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSDictionary *issue = self.filteredIssues[indexPath.row];
NSString *issueID = issue[@"id"];
if ([self.selectedIssueIDs containsObject:issueID]) {
[self.selectedIssueIDs removeObject:issueID];
} else {
[self.selectedIssueIDs addObject:issueID];
}
[self updateCounter];
[tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationNone];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 100; // adjust to your card height
}
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section {
return 10; // spacing between cells
}
- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
UIView *spacer = [[UIView alloc] init];
spacer.backgroundColor = UIColor.clearColor;
return spacer;
}
@end
// WithdrawSingleIssueViewController.h
// WithdrawSingleIssueViewController.h
#import <UIKit/UIKit.h>
@interface WithdrawSingleIssueViewController : UIViewController
@property (nonatomic, strong) NSString *issueID;
@property (nonatomic, strong) NSString *issueID; // for single issue
@property (nonatomic, strong) NSArray *selectedIssues; // for multiple issues
@property (nonatomic, strong) NSString *planID;
@property (nonatomic, strong) NSString *status;
@property (nonatomic, strong) NSString *unitName;
@property (nonatomic, strong) NSString *reference;
@property (nonatomic, strong) NSString *titleText; // "Delete Issue" or "Void Issue"
@property (nonatomic, strong) NSString *titleText; // e.g. @"Delete Issue" or @"Withdraw Issue"
@property (nonatomic, assign) BOOL isMultiWithdraw; // YES = multiple issues, NO = single
@end
// WithdrawSingleIssueViewController.mm
#import "WithdrawSingleIssueViewController.h"
#import "IssueAPIClient.h"
#import <UIKit/UIKit.h>
@interface WithdrawSingleIssueViewController ()
@interface WithdrawSingleIssueViewController () <UITableViewDelegate, UITableViewDataSource>
@property (nonatomic, strong) UIView *headerView;
@property (nonatomic, strong) UIButton *backButton;
@property (nonatomic, strong) UILabel *titleLabel;
@property (nonatomic, strong) UITextView *messageInput;
@property (nonatomic, strong) UIButton *confirmButton;
@property (nonatomic, strong) UITableView *issuesTable;
@end
......@@ -92,40 +95,69 @@
separator.backgroundColor = [UIColor colorWithWhite:0.8 alpha:1];
[self.view addSubview:separator];
self.confirmButton = [UIButton buttonWithType:UIButtonTypeSystem];
self.confirmButton.translatesAutoresizingMaskIntoConstraints = NO;
[self.confirmButton setTitle:@"Confirm" 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.titleLabel.font = [UIFont boldSystemFontOfSize:17];
self.confirmButton.layer.cornerRadius = 8;
[self.confirmButton addTarget:self action:@selector(handleConfirm)
forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:self.confirmButton];
UILayoutGuide *safe = self.view.safeAreaLayoutGuide;
[NSLayoutConstraint activateConstraints:@[
NSMutableArray *constraints = [NSMutableArray arrayWithArray:@[
[promptLabel.topAnchor constraintEqualToAnchor:self.headerView.bottomAnchor constant:20],
[promptLabel.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:20],
[promptLabel.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:-20],
[self.messageInput.topAnchor constraintEqualToAnchor:promptLabel.bottomAnchor constant:8],
[self.messageInput.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:20],
[self.messageInput.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:-20],
[self.messageInput.heightAnchor constraintEqualToConstant:120],
[separator.topAnchor constraintEqualToAnchor:self.messageInput.bottomAnchor constant:16],
[separator.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:20],
[separator.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:-20],
[separator.heightAnchor constraintEqualToConstant:1],
]];
// ✅ 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];
self.issuesTable = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStyleInsetGrouped];
self.issuesTable.translatesAutoresizingMaskIntoConstraints = NO;
self.issuesTable.dataSource = self;
self.issuesTable.delegate = self;
[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],
[self.issuesTable.heightAnchor constraintEqualToConstant:200],
]];
// confirm button below table
[constraints addObjectsFromArray:@[
[self.confirmButton.topAnchor constraintEqualToAnchor:self.issuesTable.bottomAnchor constant:30],
]];
} else {
// confirm button directly below separator
[constraints addObjectsFromArray:@[
[self.confirmButton.topAnchor constraintEqualToAnchor:separator.bottomAnchor constant:40],
]];
}
[self.confirmButton.topAnchor constraintEqualToAnchor:separator.bottomAnchor constant:40],
[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],
[self.confirmButton.heightAnchor constraintEqualToConstant:48]
[self.confirmButton.heightAnchor constraintEqualToConstant:48],
[self.confirmButton.bottomAnchor constraintLessThanOrEqualToAnchor:safe.bottomAnchor constant:-20]
]];
[NSLayoutConstraint activateConstraints:constraints];
}
#pragma mark - Actions
- (void)handleBack {
[self dismissViewControllerAnimated:YES completion:nil];
......@@ -133,27 +165,46 @@
- (void)handleConfirm {
NSString *message = self.messageInput.text ?: @"";
if (message.length == 0) {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Alert"
message:@"Please enter a message before submitting."
if (message.length == 0) {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Alert"
message:@"Please enter a message before submitting."
preferredStyle:UIAlertControllerStyleAlert];
[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];
[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.titleText containsString:@"Delete"]) {
[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
NSMutableArray *ids = [NSMutableArray array];
for (NSDictionary *issue in self.selectedIssues) {
if (issue[@"id"]) [ids addObject:issue[@"id"]];
}
[IssueAPIClient fetchVoidIssue:ids
remarks:message
completion:^(NSDictionary *data, NSError *error) {
[self handleVoidIssueResponse:data error:error];
}];
}
else if ([self.titleText containsString:@"Delete"]) {
[self requestDeleteIssue:self.issueID remarks:message];
} else {
[self requestVoidIssue:self.issueID remarks:message];
}
else {
NSArray *singleIssueArray = @[ self.issueID ?: @"" ];
[IssueAPIClient fetchVoidIssue:singleIssueArray
remarks:message
completion:^(NSDictionary *data, NSError *error) {
[self handleVoidIssueResponse:data error:error];
}];
}
}]];
[self presentViewController:confirm animated:YES completion:nil];
......@@ -196,44 +247,6 @@
}];
}
- (void)requestVoidIssue:(NSString *)issueID remarks:(NSString *)remarks {
NSLog(@"🚀 Sending VOID ISSUE for ID=%@, remarks=%@", issueID, remarks);
NSArray *issueArray = @[ issueID ?: @"" ];
[IssueAPIClient fetchVoidIssue:issueArray remarks:remarks completion:^(NSDictionary *data, NSError *error) {
if (error) {
NSLog(@"Error %@", error.localizedDescription);
return;
}
NSDictionary *appData = data[@"AppData"];
if ([appData[@"status"] isEqualToString:@"success"]) {
NSLog(@"✅ Issue deleted successfully!");
UIAlertController *successAlert = [UIAlertController alertControllerWithTitle:@"Success"
message:@"Issue deleted successfully."
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *ok = [UIAlertAction actionWithTitle:@"OK"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * _Nonnull action) {
// 🏠 Mimic AddIssue success dismissal logic
UIViewController *presenter = self.presentingViewController;
if (presenter.presentingViewController) {
[presenter.presentingViewController dismissViewControllerAnimated:YES completion:nil];
} else {
[self dismissViewControllerAnimated:YES completion:nil];
}
}];
[successAlert addAction:ok];
[self presentViewController:successAlert animated:YES completion:nil];
} else {
NSString *message = appData[@"message"] ?: @"Unknown error";
NSLog(@"⚠️ Error Message %@", message);
[self showAlertWithTitle:@"Delete Failed" message:message];
}
}];
}
# pragma mark - helper
- (void)showAlertWithTitle:(NSString *)title message:(NSString *)message {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:title
......@@ -244,4 +257,61 @@
[self presentViewController:alert animated:YES completion:nil];
}
- (void)handleVoidIssueResponse:(NSDictionary *)data error:(NSError *)error {
if (error) {
NSLog(@"❌ Void Issue Error: %@", error.localizedDescription);
[self showAlertWithTitle:@"Error" message:error.localizedDescription];
return;
}
NSDictionary *appData = data[@"AppData"];
if ([appData[@"status"] isEqualToString:@"success"]) {
NSLog(@"✅ Issues voided successfully!");
UIAlertController *successAlert = [UIAlertController alertControllerWithTitle:@"Success"
message:@"Issues withdrawn successfully."
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *ok = [UIAlertAction actionWithTitle:@"OK"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * _Nonnull action) {
// 🏠 Return to dashboard or issue list
UIViewController *presenter = self.presentingViewController;
if (presenter.presentingViewController) {
[presenter.presentingViewController dismissViewControllerAnimated:YES completion:nil];
} else {
[self dismissViewControllerAnimated:YES completion:nil];
}
}];
[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];
}
}
#pragma mark - UITableView
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.selectedIssues.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *cellId = @"WithdrawListCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellId];
if (!cell) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellId];
}
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;
return cell;
}
@end
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