Commit c8fb728f authored by Wei Han's avatar Wei Han

code update

parent 3dd910fa
...@@ -217,7 +217,13 @@ ...@@ -217,7 +217,13 @@
// 🔹 Append images // 🔹 Append images
if (images.count > 0) { if (images.count > 0) {
for (int i = 0; i < images.count; i++) { for (int i = 0; i < images.count; i++) {
UIImage *image = images[i]; id maybeImage = images[i];
if (![maybeImage isKindOfClass:[UIImage class]]) {
NSLog(@"⚠️ Skipping non-UIImage at index %d: %@", i, maybeImage);
continue; // skip invalid entries
}
UIImage *image = (UIImage *)maybeImage;
NSData *imageData = UIImageJPEGRepresentation(image, 0.8); NSData *imageData = UIImageJPEGRepresentation(image, 0.8);
if (!imageData) continue; if (!imageData) continue;
......
...@@ -240,12 +240,14 @@ ...@@ -240,12 +240,14 @@
[locationContainer.trailingAnchor constraintEqualToAnchor:content.trailingAnchor constant:-20], [locationContainer.trailingAnchor constraintEqualToAnchor:content.trailingAnchor constant:-20],
[locationContainer.heightAnchor constraintEqualToConstant:44], [locationContainer.heightAnchor constraintEqualToConstant:44],
]]; ]];
lastView = locationContainer;
// === Location Field (read-only) === // === Location Field (read-only) ===
UITextField *locationField = [[UITextField alloc] init]; UITextField *locationField = [[UITextField alloc] init];
locationField.translatesAutoresizingMaskIntoConstraints = NO; locationField.translatesAutoresizingMaskIntoConstraints = NO;
locationField.text = self.selectedLocationName ?: @"Select location"; locationField.text = self.selectedLocationName ?: @"Select location";
locationField.font = [UIFont systemFontOfSize:15]; locationField.font = [UIFont systemFontOfSize:15];
locationField.textColor = [UIColor blackColor];
locationField.enabled = NO; locationField.enabled = NO;
[locationContainer addSubview:locationField]; [locationContainer addSubview:locationField];
self.locationField = locationField; self.locationField = locationField;
...@@ -257,22 +259,22 @@ ...@@ -257,22 +259,22 @@
]]; ]];
// === Reselect Button (inside the container, right-aligned) === // === Reselect Button (inside the container, right-aligned) ===
UIButton *reselectBtn = [UIButton buttonWithType:UIButtonTypeSystem]; // UIButton *reselectBtn = [UIButton buttonWithType:UIButtonTypeSystem];
reselectBtn.translatesAutoresizingMaskIntoConstraints = NO; // reselectBtn.translatesAutoresizingMaskIntoConstraints = NO;
[reselectBtn setTitle:@"Reselect" forState:UIControlStateNormal]; // [reselectBtn setTitle:@"Reselect" forState:UIControlStateNormal];
[reselectBtn setTitleColor:[UIColor colorWithRed:0/255.0 green:109/255.0 blue:141/255.0 alpha:1] // [reselectBtn setTitleColor:[UIColor colorWithRed:0/255.0 green:109/255.0 blue:141/255.0 alpha:1]
forState:UIControlStateNormal]; // forState:UIControlStateNormal];
reselectBtn.titleLabel.font = [UIFont boldSystemFontOfSize:14]; // reselectBtn.titleLabel.font = [UIFont boldSystemFontOfSize:14];
[reselectBtn addTarget:self action:@selector(dismissSelf) forControlEvents:UIControlEventTouchUpInside]; // [reselectBtn addTarget:self action:@selector(dismissSelf) forControlEvents:UIControlEventTouchUpInside];
[locationContainer addSubview:reselectBtn]; // [locationContainer addSubview:reselectBtn];
//
[NSLayoutConstraint activateConstraints:@[ // [NSLayoutConstraint activateConstraints:@[
[reselectBtn.trailingAnchor constraintEqualToAnchor:locationContainer.trailingAnchor constant:-8], // [reselectBtn.trailingAnchor constraintEqualToAnchor:locationContainer.trailingAnchor constant:-8],
[reselectBtn.centerYAnchor constraintEqualToAnchor:locationContainer.centerYAnchor], // [reselectBtn.centerYAnchor constraintEqualToAnchor:locationContainer.centerYAnchor],
//
// shrink locationField width so it doesn’t overlap // // shrink locationField width so it doesn’t overlap
[locationField.trailingAnchor constraintEqualToAnchor:reselectBtn.leadingAnchor constant:-8], // [locationField.trailingAnchor constraintEqualToAnchor:reselectBtn.leadingAnchor constant:-8],
]]; // ]];
// Category, Type, Issue — dropdown style // Category, Type, Issue — dropdown style
NSArray *titles = @[@"*Category", @"*Type", @"*Issue"]; NSArray *titles = @[@"*Category", @"*Type", @"*Issue"];
...@@ -336,7 +338,9 @@ ...@@ -336,7 +338,9 @@
comment.layer.borderWidth = 1.0; comment.layer.borderWidth = 1.0;
comment.layer.cornerRadius = 8; comment.layer.cornerRadius = 8;
comment.layer.borderColor = [UIColor lightGrayColor].CGColor; comment.layer.borderColor = [UIColor lightGrayColor].CGColor;
comment.backgroundColor = [UIColor whiteColor];
comment.font = [UIFont systemFontOfSize:15]; comment.font = [UIFont systemFontOfSize:15];
comment.textColor = [UIColor blackColor];
[content addSubview:comment]; [content addSubview:comment];
[NSLayoutConstraint activateConstraints:@[ [NSLayoutConstraint activateConstraints:@[
[comment.topAnchor constraintEqualToAnchor:commentLabel.bottomAnchor constant:8], [comment.topAnchor constraintEqualToAnchor:commentLabel.bottomAnchor constant:8],
...@@ -381,7 +385,7 @@ ...@@ -381,7 +385,7 @@
[submit setTitleColor:UIColor.whiteColor forState:UIControlStateNormal]; [submit setTitleColor:UIColor.whiteColor forState:UIControlStateNormal];
submit.titleLabel.font = [UIFont boldSystemFontOfSize:16]; submit.titleLabel.font = [UIFont boldSystemFontOfSize:16];
submit.layer.cornerRadius = 8; submit.layer.cornerRadius = 8;
[submit addTarget:self action:@selector(handleSubmit) forControlEvents:UIControlEventTouchUpInside]; [submit addTarget:self action:@selector(handleSubmitTap) forControlEvents:UIControlEventTouchUpInside];
[content addSubview:submit]; [content addSubview:submit];
[NSLayoutConstraint activateConstraints:@[ [NSLayoutConstraint activateConstraints:@[
[submit.topAnchor constraintEqualToAnchor:lastView.bottomAnchor constant:spacing], [submit.topAnchor constraintEqualToAnchor:lastView.bottomAnchor constant:spacing],
...@@ -392,6 +396,11 @@ ...@@ -392,6 +396,11 @@
lastView = submit; lastView = submit;
[content.bottomAnchor constraintEqualToAnchor:lastView.bottomAnchor constant:spacing].active = YES; [content.bottomAnchor constraintEqualToAnchor:lastView.bottomAnchor constant:spacing].active = YES;
// 👇 Add tap gesture to dismiss keyboard
UITapGestureRecognizer *tapToDismiss = [[UITapGestureRecognizer alloc] initWithTarget:self
action:@selector(dismissKeyboard)];
tapToDismiss.cancelsTouchesInView = NO; // ensures buttons still respond
[self.scrollView addGestureRecognizer:tapToDismiss];
} }
- (UILabel *)makeLabelWithText:(NSString *)text bold:(BOOL)bold size:(CGFloat)size { - (UILabel *)makeLabelWithText:(NSString *)text bold:(BOOL)bold size:(CGFloat)size {
...@@ -740,14 +749,34 @@ didFinishPickingMediaWithInfo:(NSDictionary<UIImagePickerControllerInfoKey,id> * ...@@ -740,14 +749,34 @@ didFinishPickingMediaWithInfo:(NSDictionary<UIImagePickerControllerInfoKey,id> *
[self dismissViewControllerAnimated:YES completion:nil]; [self dismissViewControllerAnimated:YES completion:nil];
} }
- (void)handleSubmit { - (void)handleSubmitTap {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Confirm Submission"
message:@"Are you sure you want to submit this issue?"
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *confirmAction = [UIAlertAction actionWithTitle:@"Submit"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * _Nonnull action) {
[self performSubmit]; // 👈 call your existing submission logic here
}];
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"Cancel"
style:UIAlertActionStyleCancel
handler:nil];
[alert addAction:cancelAction];
[alert addAction:confirmAction];
[self presentViewController:alert animated:YES completion:nil];
}
- (void)performSubmit {
NSLog(@"Calling addIssue API:\n planID: %@\n locationID: %@\n selectedIssueID: %@\n XCoordinates: %f\n YCoordinates: %f\n commentField: %@", NSLog(@"Calling addIssue API:\n planID: %@\n locationID: %@\n selectedIssueID: %@\n XCoordinates: %f\n YCoordinates: %f\n commentField: %@",
self.planID, self.planID,
self.locationID, self.locationID,
self.selectedIssueID, self.selectedIssueID,
self.planX, self.planX,
self.planY, self.planY,
self.commentField); self.commentField.text);
NSDictionary *issueData = @{ NSDictionary *issueData = @{
@"plan_id": self.planID ?: @"0", @"plan_id": self.planID ?: @"0",
...@@ -755,25 +784,98 @@ didFinishPickingMediaWithInfo:(NSDictionary<UIImagePickerControllerInfoKey,id> * ...@@ -755,25 +784,98 @@ didFinishPickingMediaWithInfo:(NSDictionary<UIImagePickerControllerInfoKey,id> *
@"issue_setting_id": self.selectedIssueID ?: @"", @"issue_setting_id": self.selectedIssueID ?: @"",
@"position_x": [NSString stringWithFormat:@"%f", self.planX ?: 0.0], @"position_x": [NSString stringWithFormat:@"%f", self.planX ?: 0.0],
@"position_y": [NSString stringWithFormat:@"%f", self.planY ?: 0.0], @"position_y": [NSString stringWithFormat:@"%f", self.planY ?: 0.0],
@"comment": self.commentField ?: @"", @"comment": self.commentField.text ?: @"",
@"os": @"AND" @"os": @"AND"
}; };
[AddIssueAPIClient submitAddIssue:issueData [AddIssueAPIClient submitAddIssue:issueData
images:self.uploadedImages images:self.uploadedImages
completion:^(NSDictionary *response, NSError *error) { completion:^(NSDictionary *response, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{ // ensure UI updates happen on main thread
if (error) { if (error) {
NSLog(@"❌ Submission failed: %@", error.localizedDescription); NSLog(@"❌ Submission failed: %@", error.localizedDescription);
[self showAlertWithTitle:@"Submission Failed"
message:error.localizedDescription ?: @"An unknown error occurred."];
return; return;
} }
NSDictionary *appData = response[@"AppData"]; NSDictionary *appData = response[@"AppData"];
if ([appData[@"status"] isEqualToString:@"success"]) { if ([appData[@"status"] isEqualToString:@"success"]) {
NSLog(@"✅ Issue submitted successfully!"); NSLog(@"✅ Issue submitted successfully!");
// ✅ Success alert
UIAlertController *successAlert = [UIAlertController alertControllerWithTitle:@"Issue Submitted"
message:@"Your issue has been successfully added."
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *ok = [UIAlertAction actionWithTitle:@"OK"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * _Nonnull action) {
// Optionally go back or refresh dashboard
UIViewController *presenter = self.presentingViewController;
if (presenter.presentingViewController) {
[presenter.presentingViewController dismissViewControllerAnimated:YES completion:nil];
} else { } else {
NSLog(@"⚠️ API Error: %@", appData[@"message"]); [self dismissViewControllerAnimated:YES completion:nil];
}}];
[successAlert addAction:ok];
[self presentViewController:successAlert animated:YES completion:nil];
} else {
NSString *msg = appData[@"message"] ?: @"Unexpected response from server.";
NSLog(@"⚠️ API Error: %@", msg);
[self showAlertWithTitle:@"Submission Error" message:msg];
} }
});
}]; }];
} }
- (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)dismissKeyboard {
[self.view endEditing:YES];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillShow:)
name:UIKeyboardWillShowNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(keyboardWillHide:)
name:UIKeyboardWillHideNotification
object:nil];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)keyboardWillShow:(NSNotification *)notification {
NSDictionary *info = [notification userInfo];
CGRect kbFrame = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
CGFloat kbHeight = kbFrame.size.height;
UIEdgeInsets contentInsets = UIEdgeInsetsMake(0, 0, kbHeight + 20, 0);
self.scrollView.contentInset = contentInsets;
self.scrollView.scrollIndicatorInsets = contentInsets;
// 👇 Optional: make sure comment field is visible
[self.scrollView scrollRectToVisible:self.commentField.frame animated:YES];
}
- (void)keyboardWillHide:(NSNotification *)notification {
self.scrollView.contentInset = UIEdgeInsetsZero;
self.scrollView.scrollIndicatorInsets = UIEdgeInsetsZero;
}
@end @end
...@@ -291,16 +291,16 @@ ...@@ -291,16 +291,16 @@
[self.headerView addSubview:self.backButton]; [self.headerView addSubview:self.backButton];
// ❓ Help button // ❓ Help button
self.helpButton = [UIButton buttonWithType:UIButtonTypeSystem]; // self.helpButton = [UIButton buttonWithType:UIButtonTypeSystem];
self.helpButton.translatesAutoresizingMaskIntoConstraints = NO; // self.helpButton.translatesAutoresizingMaskIntoConstraints = NO;
UIImage *helpImage = [[UIImage systemImageNamed:@"questionmark.circle"] // UIImage *helpImage = [[UIImage systemImageNamed:@"questionmark.circle"]
imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; // imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
[self.helpButton setImage:helpImage forState:UIControlStateNormal]; // [self.helpButton setImage:helpImage forState:UIControlStateNormal];
self.helpButton.tintColor = UIColor.whiteColor; // self.helpButton.tintColor = UIColor.whiteColor;
[self.helpButton addTarget:self // [self.helpButton addTarget:self
action:@selector(showTutorial) // action:@selector(showTutorial)
forControlEvents:UIControlEventTouchUpInside]; // forControlEvents:UIControlEventTouchUpInside];
[self.headerView addSubview:self.helpButton]; // [self.headerView addSubview:self.helpButton];
// 🏷 Title label (project name) // 🏷 Title label (project name)
self.titleLabel = [[UILabel alloc] init]; self.titleLabel = [[UILabel alloc] init];
...@@ -344,20 +344,20 @@ ...@@ -344,20 +344,20 @@
[self.backButton.heightAnchor constraintEqualToConstant:44], [self.backButton.heightAnchor constraintEqualToConstant:44],
// Help button // Help button
[self.helpButton.trailingAnchor constraintEqualToAnchor:self.headerView.trailingAnchor constant:-8], // [self.helpButton.trailingAnchor constraintEqualToAnchor:self.headerView.trailingAnchor constant:-8],
[self.helpButton.centerYAnchor constraintEqualToAnchor:self.backButton.centerYAnchor], // [self.helpButton.centerYAnchor constraintEqualToAnchor:self.backButton.centerYAnchor],
[self.helpButton.widthAnchor constraintEqualToConstant:44], // [self.helpButton.widthAnchor constraintEqualToConstant:44],
[self.helpButton.heightAnchor constraintEqualToConstant:44], // [self.helpButton.heightAnchor constraintEqualToConstant:44],
// Title label (below buttons, left-aligned) // Title label (below buttons, left-aligned)
[self.titleLabel.topAnchor constraintEqualToAnchor:self.backButton.bottomAnchor constant:4], [self.titleLabel.topAnchor constraintEqualToAnchor:self.backButton.bottomAnchor constant:4],
[self.titleLabel.leadingAnchor constraintEqualToAnchor:self.headerView.leadingAnchor constant:16], [self.titleLabel.leadingAnchor constraintEqualToAnchor:self.headerView.leadingAnchor constant:16],
[self.titleLabel.trailingAnchor constraintLessThanOrEqualToAnchor:self.helpButton.leadingAnchor constant:-8], // [self.titleLabel.trailingAnchor constraintLessThanOrEqualToAnchor:self.helpButton.leadingAnchor constant:-8],
// Subtitle (below title, left-aligned) // Subtitle (below title, left-aligned)
[subtitleLabel.topAnchor constraintEqualToAnchor:self.titleLabel.bottomAnchor constant:2], [subtitleLabel.topAnchor constraintEqualToAnchor:self.titleLabel.bottomAnchor constant:2],
[subtitleLabel.leadingAnchor constraintEqualToAnchor:self.titleLabel.leadingAnchor], [subtitleLabel.leadingAnchor constraintEqualToAnchor:self.titleLabel.leadingAnchor],
[subtitleLabel.trailingAnchor constraintLessThanOrEqualToAnchor:self.helpButton.leadingAnchor constant:-8], // [subtitleLabel.trailingAnchor constraintLessThanOrEqualToAnchor:self.helpButton.leadingAnchor constant:-8],
]]; ]];
} }
......
...@@ -52,11 +52,11 @@ ...@@ -52,11 +52,11 @@
[self addSubview:_unitNameLabel]; [self addSubview:_unitNameLabel];
// Info button (bottom right) // Info button (bottom right)
_infoButton = [UIButton buttonWithType:UIButtonTypeCustom]; // _infoButton = [UIButton buttonWithType:UIButtonTypeCustom];
_infoButton.translatesAutoresizingMaskIntoConstraints = NO; // _infoButton.translatesAutoresizingMaskIntoConstraints = NO;
[_infoButton setImage:[UIImage systemImageNamed:@"info.circle"] forState:UIControlStateNormal]; // [_infoButton setImage:[UIImage systemImageNamed:@"info.circle"] forState:UIControlStateNormal];
_infoButton.tintColor = [UIColor whiteColor]; // _infoButton.tintColor = [UIColor whiteColor];
[self addSubview:_infoButton]; // [self addSubview:_infoButton];
// Layout constraints // Layout constraints
[NSLayoutConstraint activateConstraints:@[ [NSLayoutConstraint activateConstraints:@[
...@@ -79,10 +79,10 @@ ...@@ -79,10 +79,10 @@
[_unitNameLabel.bottomAnchor constraintEqualToAnchor:self.bottomAnchor constant:-16], [_unitNameLabel.bottomAnchor constraintEqualToAnchor:self.bottomAnchor constant:-16],
// Info button bottom-right // Info button bottom-right
[_infoButton.trailingAnchor constraintEqualToAnchor:self.trailingAnchor constant:-16], // [_infoButton.trailingAnchor constraintEqualToAnchor:self.trailingAnchor constant:-16],
[_infoButton.bottomAnchor constraintEqualToAnchor:self.bottomAnchor constant:-18], // [_infoButton.bottomAnchor constraintEqualToAnchor:self.bottomAnchor constant:-18],
[_infoButton.widthAnchor constraintEqualToConstant:24], // [_infoButton.widthAnchor constraintEqualToConstant:24],
[_infoButton.heightAnchor constraintEqualToConstant:24], // [_infoButton.heightAnchor constraintEqualToConstant:24],
]]; ]];
} }
......
...@@ -24,6 +24,8 @@ ...@@ -24,6 +24,8 @@
@property (nonatomic, strong) NSString *projectName; @property (nonatomic, strong) NSString *projectName;
@property (nonatomic, strong) UIImage *headerBackgroundImage; @property (nonatomic, strong) UIImage *headerBackgroundImage;
@property (nonatomic, strong) NSArray *announcements; @property (nonatomic, strong) NSArray *announcements;
@property (nonatomic, strong) UIView *loadingOverlay;
@property (nonatomic, strong) UIActivityIndicatorView *spinner;
@end @end
...@@ -47,49 +49,49 @@ ...@@ -47,49 +49,49 @@
self.projectCode = @"PTD197656"; self.projectCode = @"PTD197656";
self.projectName = @"Ara E`Residence Dev"; self.projectName = @"Ara E`Residence Dev";
[DashboardAPIClient fetchDashboardInfo:^(NSDictionary *data, NSError *error) { [self refreshDashboardContent];
if (error) { }
NSLog(@"❌ fetchDashboardInfo error: %@", error.localizedDescription);
return;
}
NSLog(@"🧩 Dashboard Info response received"); #pragma mark - Refresh Logic
[self processDashboardResponse:data]; - (void)refreshDashboardContent {
NSLog(@"🔁 Refreshing dashboard content...");
// 💾 Optionally, save full JSON to a file for reference dispatch_group_t group = dispatch_group_create();
NSString *docsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject; __block NSDictionary *dashboardData = nil;
NSString *filePath = [docsPath stringByAppendingPathComponent:@"DashboardInfoResponse.txt"]; __block NSDictionary *announcementData = nil;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:data options:NSJSONWritingPrettyPrinted error:nil];
[jsonData writeToFile:filePath atomically:YES]; dispatch_group_enter(group);
NSLog(@"📁 Dashboard API response saved to %@", filePath); [DashboardAPIClient fetchDashboardInfo:^(NSDictionary *data, NSError *error) {
if (!error) dashboardData = data;
dispatch_group_leave(group);
}]; }];
dispatch_group_enter(group);
[DashboardAPIClient fetchAnnouncement:^(NSDictionary *data, NSError *error) { [DashboardAPIClient fetchAnnouncement:^(NSDictionary *data, NSError *error) {
if (error) { if (!error) announcementData = data;
NSLog(@"❌ fetchAnnouncement error: %@", error.localizedDescription); dispatch_group_leave(group);
return; }];
}
NSLog(@"🗞️ Announcement response received"); dispatch_group_notify(group, dispatch_get_main_queue(), ^{
if (dashboardData) [self processDashboardResponse:dashboardData];
if (announcementData) [self processAnnouncementResponse:announcementData];
NSLog(@"✅ Dashboard refresh complete");
});
}
// 🔍 Optional: Log the whole JSON structure for debugging
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:data options:NSJSONWritingPrettyPrinted error:nil];
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
NSLog(@"%@", jsonString);
// 💾 Save to file for offline reference - (void)handlePullToRefresh:(UIRefreshControl *)refreshControl {
NSString *docsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject; NSLog(@"🔄 Pull-to-refresh triggered");
NSString *filePath = [docsPath stringByAppendingPathComponent:@"AnnouncementResponse.txt"];
[jsonData writeToFile:filePath atomically:YES];
NSLog(@"📁 Announcement response saved to %@", filePath);
// ✅ Process and show popup announcements [self refreshDashboardContent];
[self processAnnouncementResponse:data];
}]; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)),
dispatch_get_main_queue(), ^{
[refreshControl endRefreshing];
});
} }
#pragma mark - Layout #pragma mark - Layout
- (void)viewDidLayoutSubviews { - (void)viewDidLayoutSubviews {
NSLog(@"Header frame: %@", NSStringFromCGRect(_headerView.frame)); NSLog(@"Header frame: %@", NSStringFromCGRect(_headerView.frame));
[super viewDidLayoutSubviews]; [super viewDidLayoutSubviews];
...@@ -105,7 +107,6 @@ ...@@ -105,7 +107,6 @@
} }
#pragma mark - Header #pragma mark - Header
- (void)setupHeader { - (void)setupHeader {
CGFloat headerHeight = 220.0; CGFloat headerHeight = 220.0;
_headerView = [[DashboardHeaderView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, headerHeight)]; _headerView = [[DashboardHeaderView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, headerHeight)];
...@@ -131,33 +132,33 @@ ...@@ -131,33 +132,33 @@
}); });
[self.view addSubview:_headerView]; [self.view addSubview:_headerView];
UIButton *backButton = [UIButton buttonWithType:UIButtonTypeSystem]; // UIButton *backButton = [UIButton buttonWithType:UIButtonTypeSystem];
backButton.translatesAutoresizingMaskIntoConstraints = NO; // backButton.translatesAutoresizingMaskIntoConstraints = NO;
UIImage *backImage = [[UIImage systemImageNamed:@"chevron.left"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; // UIImage *backImage = [[UIImage systemImageNamed:@"chevron.left"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
[backButton setImage:backImage forState:UIControlStateNormal]; // [backButton setImage:backImage forState:UIControlStateNormal];
backButton.tintColor = [UIColor whiteColor]; // backButton.tintColor = [UIColor whiteColor];
[backButton addTarget:self action:@selector(handleBackButtonTapped) forControlEvents:UIControlEventTouchUpInside]; // [backButton addTarget:self action:@selector(handleBackButtonTapped) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:backButton]; // [self.view addSubview:backButton];
UIButton *menuButton = [UIButton buttonWithType:UIButtonTypeSystem]; // UIButton *menuButton = [UIButton buttonWithType:UIButtonTypeSystem];
menuButton.translatesAutoresizingMaskIntoConstraints = NO; // menuButton.translatesAutoresizingMaskIntoConstraints = NO;
UIImage *menuImage = [[UIImage systemImageNamed:@"line.3.horizontal"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate]; // UIImage *menuImage = [[UIImage systemImageNamed:@"line.3.horizontal"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
[menuButton setImage:menuImage forState:UIControlStateNormal]; // [menuButton setImage:menuImage forState:UIControlStateNormal];
menuButton.tintColor = [UIColor whiteColor]; // menuButton.tintColor = [UIColor whiteColor];
[menuButton addTarget:self action:@selector(handleMenuButtonTapped) forControlEvents:UIControlEventTouchUpInside]; // [menuButton addTarget:self action:@selector(handleMenuButtonTapped) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:menuButton]; // [self.view addSubview:menuButton];
[NSLayoutConstraint activateConstraints:@[ // [NSLayoutConstraint activateConstraints:@[
[backButton.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:8], // [backButton.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:8],
[backButton.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor constant:4], // [backButton.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor constant:4],
[backButton.widthAnchor constraintEqualToConstant:44], // [backButton.widthAnchor constraintEqualToConstant:44],
[backButton.heightAnchor constraintEqualToConstant:44], // [backButton.heightAnchor constraintEqualToConstant:44],
//
[menuButton.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:-8], // [menuButton.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:-8],
[menuButton.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor constant:4], // [menuButton.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor constant:4],
[menuButton.widthAnchor constraintEqualToConstant:44], // [menuButton.widthAnchor constraintEqualToConstant:44],
[menuButton.heightAnchor constraintEqualToConstant:44], // [menuButton.heightAnchor constraintEqualToConstant:44],
]]; // ]];
} }
- (void)handleBackButtonTapped { - (void)handleBackButtonTapped {
...@@ -174,7 +175,6 @@ ...@@ -174,7 +175,6 @@
} }
#pragma mark - Footer #pragma mark - Footer
- (void)setupFooter { - (void)setupFooter {
CGFloat footerHeight = 80.0; CGFloat footerHeight = 80.0;
CGFloat bottomInset = self.view.safeAreaInsets.bottom; CGFloat bottomInset = self.view.safeAreaInsets.bottom;
...@@ -241,7 +241,6 @@ ...@@ -241,7 +241,6 @@
} }
#pragma mark - Scroll Content #pragma mark - Scroll Content
- (void)setupScrollContent { - (void)setupScrollContent {
CGFloat topOffset = 220.0; CGFloat topOffset = 220.0;
_scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake( _scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(
...@@ -258,6 +257,17 @@ ...@@ -258,6 +257,17 @@
_scrollView.contentInset = UIEdgeInsetsMake(0, 0, footerHeight, 0); _scrollView.contentInset = UIEdgeInsetsMake(0, 0, footerHeight, 0);
_scrollView.scrollIndicatorInsets = _scrollView.contentInset; _scrollView.scrollIndicatorInsets = _scrollView.contentInset;
// === 🌀 Add Pull-to-Refresh ===
UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init];
[refreshControl addTarget:self
action:@selector(handlePullToRefresh:)
forControlEvents:UIControlEventValueChanged];
if (@available(iOS 10.0, *)) {
_scrollView.refreshControl = refreshControl;
} else {
[_scrollView addSubview:refreshControl];
}
[self.view addSubview:_scrollView]; [self.view addSubview:_scrollView];
_contentContainer = [[UIView alloc] initWithFrame:CGRectMake(0, 0, _scrollView.bounds.size.width, 0)]; _contentContainer = [[UIView alloc] initWithFrame:CGRectMake(0, 0, _scrollView.bounds.size.width, 0)];
...@@ -346,7 +356,6 @@ ...@@ -346,7 +356,6 @@
} }
#pragma mark - Dashboard Data Processor #pragma mark - Dashboard Data Processor
- (void)processDashboardResponse:(NSDictionary *)data { - (void)processDashboardResponse:(NSDictionary *)data {
NSDictionary *appData = data[@"AppData"]; NSDictionary *appData = data[@"AppData"];
NSDictionary *mainData = data[@"Data"]; NSDictionary *mainData = data[@"Data"];
...@@ -467,8 +476,6 @@ ...@@ -467,8 +476,6 @@
// } // }
} }
- (void)refreshDashboardSectionsWithData:(NSDictionary *)mainData { - (void)refreshDashboardSectionsWithData:(NSDictionary *)mainData {
NSLog(@"🔄 Refreshing dashboard sections..."); NSLog(@"🔄 Refreshing dashboard sections...");
...@@ -525,6 +532,7 @@ ...@@ -525,6 +532,7 @@
UILabel *title = [[UILabel alloc] initWithFrame:CGRectMake(16, 0, 200, 24)]; UILabel *title = [[UILabel alloc] initWithFrame:CGRectMake(16, 0, 200, 24)];
title.text = @"Project Update"; title.text = @"Project Update";
title.font = [UIFont boldSystemFontOfSize:18]; title.font = [UIFont boldSystemFontOfSize:18];
title.textColor = [UIColor blackColor];
title.userInteractionEnabled = YES; // 👈 enable taps title.userInteractionEnabled = YES; // 👈 enable taps
UITapGestureRecognizer *titleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(sectionHeaderTapped:)]; UITapGestureRecognizer *titleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(sectionHeaderTapped:)];
...@@ -567,6 +575,7 @@ ...@@ -567,6 +575,7 @@
title.text = @"Appointments"; title.text = @"Appointments";
title.font = [UIFont boldSystemFontOfSize:18]; title.font = [UIFont boldSystemFontOfSize:18];
[section addSubview:title]; [section addSubview:title];
title.textColor = [UIColor blackColor];
title.userInteractionEnabled = YES; // 👈 enable taps title.userInteractionEnabled = YES; // 👈 enable taps
UITapGestureRecognizer *titleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(sectionHeaderTapped:)]; UITapGestureRecognizer *titleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(sectionHeaderTapped:)];
...@@ -604,6 +613,7 @@ ...@@ -604,6 +613,7 @@
title.text = @"Issue Updates"; title.text = @"Issue Updates";
title.font = [UIFont boldSystemFontOfSize:18]; title.font = [UIFont boldSystemFontOfSize:18];
[section addSubview:title]; [section addSubview:title];
title.textColor = [UIColor blackColor];
title.userInteractionEnabled = YES; // 👈 enable taps title.userInteractionEnabled = YES; // 👈 enable taps
UITapGestureRecognizer *titleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(sectionHeaderTapped:)]; UITapGestureRecognizer *titleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(sectionHeaderTapped:)];
...@@ -675,6 +685,7 @@ ...@@ -675,6 +685,7 @@
title.text = item[@"title"] ?: @"Untitled announcement"; title.text = item[@"title"] ?: @"Untitled announcement";
title.numberOfLines = 3; title.numberOfLines = 3;
title.font = [UIFont boldSystemFontOfSize:16]; title.font = [UIFont boldSystemFontOfSize:16];
title.textColor = [UIColor blackColor];
[card addSubview:title]; [card addSubview:title];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleAnnouncementTap:)]; UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleAnnouncementTap:)];
...@@ -730,6 +741,7 @@ ...@@ -730,6 +741,7 @@
timeLabel.textAlignment = NSTextAlignmentRight; timeLabel.textAlignment = NSTextAlignmentRight;
timeLabel.text = startTime; timeLabel.text = startTime;
timeLabel.font = [UIFont boldSystemFontOfSize:15]; timeLabel.font = [UIFont boldSystemFontOfSize:15];
timeLabel.textColor = [UIColor grayColor];
[card addSubview:timeLabel]; [card addSubview:timeLabel];
// === Description text === // === Description text ===
...@@ -945,7 +957,6 @@ ...@@ -945,7 +957,6 @@
} }
#pragma mark - Individual Card Taps #pragma mark - Individual Card Taps
- (void)handleAnnouncementTap:(UITapGestureRecognizer *)gesture { - (void)handleAnnouncementTap:(UITapGestureRecognizer *)gesture {
UIView *card = gesture.view; UIView *card = gesture.view;
[self animateTapForView:card]; [self animateTapForView:card];
...@@ -973,7 +984,6 @@ ...@@ -973,7 +984,6 @@
} }
#pragma mark - Card Factory with Tap Animation #pragma mark - Card Factory with Tap Animation
- (UIView *)createCardWithText:(NSString *)text frame:(CGRect)frame { - (UIView *)createCardWithText:(NSString *)text frame:(CGRect)frame {
UIView *card = [[UIView alloc] initWithFrame:frame]; UIView *card = [[UIView alloc] initWithFrame:frame];
card.backgroundColor = [UIColor whiteColor]; card.backgroundColor = [UIColor whiteColor];
...@@ -997,7 +1007,6 @@ ...@@ -997,7 +1007,6 @@
} }
#pragma mark - Tap Handlers #pragma mark - Tap Handlers
- (void)sectionHeaderTapped:(UITapGestureRecognizer *)gesture { - (void)sectionHeaderTapped:(UITapGestureRecognizer *)gesture {
UILabel *header = (UILabel *)gesture.view; UILabel *header = (UILabel *)gesture.view;
[self animateTapForView:header]; [self animateTapForView:header];
...@@ -1022,4 +1031,40 @@ ...@@ -1022,4 +1031,40 @@
}]; }];
} }
#pragma mark - Loading Overlay
- (void)showLoadingOverlay:(NSString *)message {
if (self.loadingOverlay.superview) return; // already showing
UIView *overlay = [[UIView alloc] initWithFrame:self.view.bounds];
overlay.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.4];
overlay.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc]
initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleLarge];
spinner.center = overlay.center;
spinner.color = UIColor.whiteColor;
[spinner startAnimating];
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0, CGRectGetMaxY(spinner.frame) + 16,
overlay.bounds.size.width, 30)];
label.text = message ?: @"Loading...";
label.textAlignment = NSTextAlignmentCenter;
label.textColor = UIColor.whiteColor;
label.font = [UIFont boldSystemFontOfSize:16];
[overlay addSubview:spinner];
[overlay addSubview:label];
[self.view addSubview:overlay];
self.loadingOverlay = overlay;
self.spinner = spinner;
}
- (void)hideLoadingOverlay {
[self.spinner stopAnimating];
[self.loadingOverlay removeFromSuperview];
self.loadingOverlay = nil;
self.spinner = nil;
}
@end @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