Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
W
weihan-plugin
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Wei Han
weihan-plugin
Commits
c8fb728f
Commit
c8fb728f
authored
Oct 28, 2025
by
Wei Han
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
code update
parent
3dd910fa
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
289 additions
and
136 deletions
+289
-136
QmsPluginFramework
...ios-arm64/QmsPluginFramework.framework/QmsPluginFramework
+0
-0
QmsPluginFramework
...simulator/QmsPluginFramework.framework/QmsPluginFramework
+0
-0
AddIssueAPIClient.mm
...ramework/QmsPluginFramework/AddIssue/AddIssueAPIClient.mm
+7
-1
DetailsViewController.mm
...work/QmsPluginFramework/AddIssue/DetailsViewController.mm
+144
-42
PlanViewController.mm
...amework/QmsPluginFramework/AddIssue/PlanViewController.mm
+16
-16
DashboardHeaderView.mm
...ework/QmsPluginFramework/Dashboard/DashboardHeaderView.mm
+9
-9
DashboardViewController.mm
...k/QmsPluginFramework/Dashboard/DashboardViewController.mm
+113
-68
QmsPluginFramework
...ios-arm64/QmsPluginFramework.framework/QmsPluginFramework
+0
-0
QmsPluginFramework
...simulator/QmsPluginFramework.framework/QmsPluginFramework
+0
-0
No files found.
framework/QmsPluginFramework/QmsPluginFramework.xcframework/ios-arm64/QmsPluginFramework.framework/QmsPluginFramework
View file @
c8fb728f
No preview for this file type
framework/QmsPluginFramework/QmsPluginFramework.xcframework/ios-arm64_x86_64-simulator/QmsPluginFramework.framework/QmsPluginFramework
View file @
c8fb728f
No preview for this file type
framework/QmsPluginFramework/QmsPluginFramework/AddIssue/AddIssueAPIClient.mm
View file @
c8fb728f
...
@@ -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;
...
...
framework/QmsPluginFramework/QmsPluginFramework/AddIssue/DetailsViewController.mm
View file @
c8fb728f
...
@@ -206,7 +206,7 @@
...
@@ -206,7 +206,7 @@
[content addSubview:forLabel];
[content addSubview:forLabel];
[forLabel.topAnchor constraintEqualToAnchor:content.topAnchor constant:spacing].active = YES;
[forLabel.topAnchor constraintEqualToAnchor:content.topAnchor constant:spacing].active = YES;
[forLabel.leadingAnchor constraintEqualToAnchor:content.leadingAnchor constant:20].active = YES;
[forLabel.leadingAnchor constraintEqualToAnchor:content.leadingAnchor constant:20].active = YES;
// 🧩 Build dynamic text safely
// 🧩 Build dynamic text safely
NSString *unitText = @"";
NSString *unitText = @"";
if (self.projectName && self.projectCode) {
if (self.projectName && self.projectCode) {
...
@@ -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(handleSubmit
Tap
) 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],
...
@@ -391,7 +395,12 @@
...
@@ -391,7 +395,12 @@
]];
]];
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,40 +749,133 @@ didFinishPickingMediaWithInfo:(NSDictionary<UIImagePickerControllerInfoKey,id> *
...
@@ -740,40 +749,133 @@ didFinishPickingMediaWithInfo:(NSDictionary<UIImagePickerControllerInfoKey,id> *
[self dismissViewControllerAnimated:YES completion:nil];
[self dismissViewControllerAnimated:YES completion:nil];
}
}
- (void)handleSubmit {
- (void)handleSubmitTap {
NSLog(@"Calling addIssue API:\n planID: %@\n locationID: %@\n selectedIssueID: %@\n XCoordinates: %f\n YCoordinates: %f\n commentField: %@",
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Confirm Submission"
self.planID,
message:@"Are you sure you want to submit this issue?"
self.locationID,
preferredStyle:UIAlertControllerStyleAlert];
self.selectedIssueID,
self.planX,
UIAlertAction *confirmAction = [UIAlertAction actionWithTitle:@"Submit"
self.planY,
style:UIAlertActionStyleDefault
self.commentField);
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: %@",
self.planID,
self.locationID,
self.selectedIssueID,
self.planX,
self.planY,
self.commentField.text);
NSDictionary *issueData = @{
NSDictionary *issueData = @{
@"plan_id": self.planID ?: @"0",
@"plan_id": self.planID ?: @"0",
@"location_id": self.locationID ?: @"0",
@"location_id": self.locationID ?: @"0",
@"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) {
if (error) {
dispatch_async(dispatch_get_main_queue(), ^{ // ensure UI updates happen on main thread
NSLog(@"❌ Submission failed: %@", error.localizedDescription);
if (error) {
return;
NSLog(@"❌ Submission failed: %@", error.localizedDescription);
}
[self showAlertWithTitle:@"Submission Failed"
message:error.localizedDescription ?: @"An unknown error occurred."];
NSDictionary *appData = response[@"AppData"];
return;
if ([appData[@"status"] isEqualToString:@"success"]) {
}
NSLog(@"✅ Issue submitted successfully!");
} else {
NSDictionary *appData = response[@"AppData"];
NSLog(@"⚠️ API Error: %@", appData[@"message"]);
if ([appData[@"status"] isEqualToString:@"success"]) {
}
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 {
[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
framework/QmsPluginFramework/QmsPluginFramework/AddIssue/PlanViewController.mm
View file @
c8fb728f
...
@@ -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],
]];
]];
}
}
...
...
framework/QmsPluginFramework/QmsPluginFramework/Dashboard/DashboardHeaderView.mm
View file @
c8fb728f
...
@@ -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],
]];
]];
}
}
...
...
framework/QmsPluginFramework/QmsPluginFramework/Dashboard/DashboardViewController.mm
View file @
c8fb728f
...
@@ -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;
#pragma mark - Refresh Logic
}
- (void)refreshDashboardContent {
NSLog(@"🔁 Refreshing dashboard content...");
NSLog(@"🧩 Dashboard Info response received");
dispatch_group_t group = dispatch_group_create();
[self processDashboardResponse:data];
__block NSDictionary *dashboardData = nil;
__block NSDictionary *announcementData = nil;
// 💾 Optionally, save full JSON to a file for reference
dispatch_group_enter(group);
NSString *docsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
[DashboardAPIClient fetchDashboardInfo:^(NSDictionary *data, NSError *error) {
NSString *filePath = [docsPath stringByAppendingPathComponent:@"DashboardInfoResponse.txt"];
if (!error) dashboardData = data;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:data options:NSJSONWritingPrettyPrinted error:nil];
dispatch_group_leave(group);
[jsonData writeToFile:filePath atomically:YES];
NSLog(@"📁 Dashboard API response saved to %@", filePath);
}];
}];
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
ios/QmsPluginFramework.xcframework/ios-arm64/QmsPluginFramework.framework/QmsPluginFramework
View file @
c8fb728f
No preview for this file type
ios/QmsPluginFramework.xcframework/ios-arm64_x86_64-simulator/QmsPluginFramework.framework/QmsPluginFramework
View file @
c8fb728f
No preview for this file type
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment