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
2ac4e245
Commit
2ac4e245
authored
Oct 14, 2025
by
Wei Han
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
UI code update
parent
db23c5fb
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
843 additions
and
103 deletions
+843
-103
QmsPluginFramework
...ios-arm64/QmsPluginFramework.framework/QmsPluginFramework
+0
-0
QmsPluginFramework
...simulator/QmsPluginFramework.framework/QmsPluginFramework
+0
-0
DetailsViewController.h
...ework/QmsPluginFramework/AddIssue/DetailsViewController.h
+3
-0
DetailsViewController.mm
...work/QmsPluginFramework/AddIssue/DetailsViewController.mm
+399
-40
PlanViewController.mm
...amework/QmsPluginFramework/AddIssue/PlanViewController.mm
+110
-4
DashboardAPIClient.h
...amework/QmsPluginFramework/Dashboard/DashboardAPIClient.h
+12
-0
DashboardAPIClient.mm
...mework/QmsPluginFramework/Dashboard/DashboardAPIClient.mm
+70
-0
DashboardHeaderView.mm
...ework/QmsPluginFramework/Dashboard/DashboardHeaderView.mm
+1
-0
DashboardViewController.mm
...k/QmsPluginFramework/Dashboard/DashboardViewController.mm
+248
-59
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 @
2ac4e245
No preview for this file type
framework/QmsPluginFramework/QmsPluginFramework.xcframework/ios-arm64_x86_64-simulator/QmsPluginFramework.framework/QmsPluginFramework
View file @
2ac4e245
No preview for this file type
framework/QmsPluginFramework/QmsPluginFramework/AddIssue/DetailsViewController.h
View file @
2ac4e245
...
@@ -4,4 +4,7 @@
...
@@ -4,4 +4,7 @@
#import <UIKit/UIKit.h>
#import <UIKit/UIKit.h>
@interface
AddIssueDetailsViewController
:
UIViewController
@interface
AddIssueDetailsViewController
:
UIViewController
@property
(
nonatomic
,
strong
)
NSString
*
selectedLocationName
;
@property
(
nonatomic
,
strong
)
NSMutableArray
<
UIImage
*>
*
uploadedImages
;
@end
@end
framework/QmsPluginFramework/QmsPluginFramework/AddIssue/DetailsViewController.mm
View file @
2ac4e245
#import "DetailsViewController.h"
#import "DetailsViewController.h"
@interface AddIssueDetailsViewController ()
@interface AddIssueDetailsViewController ()
<UIImagePickerControllerDelegate, UINavigationControllerDelegate>
@property (nonatomic, strong) UIView *headerView;
@property (nonatomic, strong) UIView *headerView;
@property (nonatomic, strong) UIScrollView *scrollView;
@property (nonatomic, strong) UIScrollView *scrollView;
@property (nonatomic, strong) UITextField *locationField;
@property (nonatomic, strong) UITextField *locationField;
...
@@ -18,6 +18,11 @@
...
@@ -18,6 +18,11 @@
// Create all views (don’t position them yet)
// Create all views (don’t position them yet)
[self createHeader];
[self createHeader];
[self createForm];
[self createForm];
// Set location if passed from previous screen
if (self.selectedLocationName && self.selectedLocationName.length > 0) {
self.locationField.text = self.selectedLocationName;
}
self.uploadedImages = [NSMutableArray arrayWithObjects:[NSNull null], [NSNull null], [NSNull null], nil];
}
}
- (void)viewDidLayoutSubviews {
- (void)viewDidLayoutSubviews {
...
@@ -37,14 +42,7 @@
...
@@ -37,14 +42,7 @@
// Scroll view layout (below header)
// Scroll view layout (below header)
CGFloat scrollY = topInset + headerHeight;
CGFloat scrollY = topInset + headerHeight;
self.scrollView.frame = CGRectMake(0, scrollY,
self.scrollView.frame = CGRectMake(0, scrollY, self.view.bounds.size.width, self.view.bounds.size.height - scrollY);
self.view.bounds.size.width,
self.view.bounds.size.height - scrollY);
// Adjust internal subviews (since width may have changed)
self.locationField.frame = CGRectMake(20, 50, self.view.bounds.size.width - 40, 40);
self.commentField.frame = CGRectMake(20, 140, self.view.bounds.size.width - 40, 100);
self.submitButton.frame = CGRectMake(20, 260, self.view.bounds.size.width - 40, 48);
}
}
#pragma mark - Setup
#pragma mark - Setup
...
@@ -132,37 +130,397 @@
...
@@ -132,37 +130,397 @@
}
}
- (void)createForm {
- (void)createForm {
self.scrollView = [[UIScrollView alloc] init];
UIScrollView *scroll = [[UIScrollView alloc] init];
[self.view addSubview:self.scrollView];
scroll.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:scroll];
UILabel *locationLabel = [[UILabel alloc] initWithFrame:CGRectMake(20, 20, 200, 20)];
self.scrollView = scroll;
locationLabel.text = @"*Location";
locationLabel.font = [UIFont boldSystemFontOfSize:16];
UILayoutGuide *safe = self.view.safeAreaLayoutGuide;
[self.scrollView addSubview:locationLabel];
[NSLayoutConstraint activateConstraints:@[
[scroll.topAnchor constraintEqualToAnchor:self.headerView.bottomAnchor],
self.locationField = [[UITextField alloc] initWithFrame:CGRectZero];
[scroll.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
self.locationField.borderStyle = UITextBorderStyleRoundedRect;
[scroll.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
[self.scrollView addSubview:self.locationField];
[scroll.bottomAnchor constraintEqualToAnchor:safe.bottomAnchor],
]];
UILabel *commentLabel = [[UILabel alloc] initWithFrame:CGRectMake(20, 110, 200, 20)];
commentLabel.text = @"Comment";
UIView *content = [[UIView alloc] init];
commentLabel.font = [UIFont boldSystemFontOfSize:16];
content.translatesAutoresizingMaskIntoConstraints = NO;
[self.scrollView addSubview:commentLabel];
[scroll addSubview:content];
[NSLayoutConstraint activateConstraints:@[
self.commentField = [[UITextView alloc] initWithFrame:CGRectZero];
[content.topAnchor constraintEqualToAnchor:scroll.topAnchor],
self.commentField.layer.borderColor = [UIColor lightGrayColor].CGColor;
[content.leadingAnchor constraintEqualToAnchor:scroll.leadingAnchor],
self.commentField.layer.borderWidth = 1.0;
[content.trailingAnchor constraintEqualToAnchor:scroll.trailingAnchor],
self.commentField.layer.cornerRadius = 8;
[content.bottomAnchor constraintEqualToAnchor:scroll.bottomAnchor],
[self.scrollView addSubview:self.commentField];
[content.widthAnchor constraintEqualToAnchor:scroll.widthAnchor],
]];
self.submitButton = [UIButton buttonWithType:UIButtonTypeSystem];
self.submitButton.frame = CGRectZero;
CGFloat spacing = 16;
self.submitButton.backgroundColor = [UIColor colorWithRed:0/255.0 green:109/255.0 blue:141/255.0 alpha:1];
UIView *lastView = nil;
[self.submitButton setTitle:@"Submit" forState:UIControlStateNormal];
[self.submitButton setTitleColor:UIColor.whiteColor forState:UIControlStateNormal];
// “For” label
self.submitButton.layer.cornerRadius = 8;
UILabel *forLabel = [self makeLabelWithText:@"For" bold:NO size:14];
[self.submitButton addTarget:self action:@selector(handleSubmit) forControlEvents:UIControlEventTouchUpInside];
[content addSubview:forLabel];
[self.scrollView addSubview:self.submitButton];
[forLabel.topAnchor constraintEqualToAnchor:content.topAnchor constant:spacing].active = YES;
[forLabel.leadingAnchor constraintEqualToAnchor:content.leadingAnchor constant:20].active = YES;
UILabel *unitLabel = [self makeLabelWithText:@"Ara E Residence Dev, C-L(2)" bold:YES size:16];
[content addSubview:unitLabel];
[unitLabel.topAnchor constraintEqualToAnchor:forLabel.bottomAnchor constant:4].active = YES;
[unitLabel.leadingAnchor constraintEqualToAnchor:forLabel.leadingAnchor].active = YES;
lastView = unitLabel;
// Location section
UILabel *locationTitle = [self makeLabelWithText:@"*Location" bold:YES size:15];
[content addSubview:locationTitle];
[locationTitle.topAnchor constraintEqualToAnchor:lastView.bottomAnchor constant:spacing].active = YES;
[locationTitle.leadingAnchor constraintEqualToAnchor:forLabel.leadingAnchor].active = YES;
UIView *locationContainer = [self makeRoundedBox];
[content addSubview:locationContainer];
[NSLayoutConstraint activateConstraints:@[
[locationContainer.topAnchor constraintEqualToAnchor:locationTitle.bottomAnchor constant:8],
[locationContainer.leadingAnchor constraintEqualToAnchor:content.leadingAnchor constant:20],
[locationContainer.trailingAnchor constraintEqualToAnchor:content.trailingAnchor constant:-20],
[locationContainer.heightAnchor constraintEqualToConstant:44],
]];
UITextField *locationField = [[UITextField alloc] init];
locationField.translatesAutoresizingMaskIntoConstraints = NO;
locationField.text = self.selectedLocationName ?: @"Select location";
locationField.font = [UIFont systemFontOfSize:15];
locationField.enabled = NO;
[locationContainer addSubview:locationField];
self.locationField = locationField;
UIButton *reselectBtn = [UIButton buttonWithType:UIButtonTypeSystem];
reselectBtn.translatesAutoresizingMaskIntoConstraints = NO;
[reselectBtn setTitle:@"Reselect" forState:UIControlStateNormal];
[reselectBtn setTitleColor:[UIColor colorWithRed:0/255.0 green:109/255.0 blue:141/255.0 alpha:1] forState:UIControlStateNormal];
reselectBtn.titleLabel.font = [UIFont boldSystemFontOfSize:14];
[reselectBtn addTarget:self action:@selector(dismissSelf) forControlEvents:UIControlEventTouchUpInside];
[locationContainer addSubview:reselectBtn];
[NSLayoutConstraint activateConstraints:@[
[locationField.leadingAnchor constraintEqualToAnchor:locationContainer.leadingAnchor constant:8],
[locationField.centerYAnchor constraintEqualToAnchor:locationContainer.centerYAnchor],
[reselectBtn.trailingAnchor constraintEqualToAnchor:locationContainer.trailingAnchor constant:-4],
[reselectBtn.centerYAnchor constraintEqualToAnchor:locationContainer.centerYAnchor],
[locationField.trailingAnchor constraintEqualToAnchor:reselectBtn.leadingAnchor constant:-12],
]];
lastView = locationContainer;
// Category, Type, Issue — dropdown style
NSArray *titles = @[@"*Category", @"*Type", @"*Issue"];
NSMutableArray *dropdowns = [NSMutableArray array];
for (NSString *t in titles) {
UILabel *lbl = [self makeLabelWithText:t bold:YES size:15];
[content addSubview:lbl];
[lbl.topAnchor constraintEqualToAnchor:lastView.bottomAnchor constant:spacing].active = YES;
[lbl.leadingAnchor constraintEqualToAnchor:forLabel.leadingAnchor].active = YES;
UIButton *dropdown = [self makeDropdownWithPlaceholder:[NSString stringWithFormat:@"- Select %@ -", [t componentsSeparatedByString:@" "].lastObject]];
[content addSubview:dropdown];
[NSLayoutConstraint activateConstraints:@[
[dropdown.topAnchor constraintEqualToAnchor:lbl.bottomAnchor constant:8],
[dropdown.leadingAnchor constraintEqualToAnchor:content.leadingAnchor constant:20],
[dropdown.trailingAnchor constraintEqualToAnchor:content.trailingAnchor constant:-20],
[dropdown.heightAnchor constraintEqualToConstant:44],
]];
[dropdowns addObject:dropdown];
lastView = dropdown;
}
// Comment box
UILabel *commentLabel = [self makeLabelWithText:@"Comment" bold:YES size:15];
[content addSubview:commentLabel];
[commentLabel.topAnchor constraintEqualToAnchor:lastView.bottomAnchor constant:spacing].active = YES;
[commentLabel.leadingAnchor constraintEqualToAnchor:forLabel.leadingAnchor].active = YES;
UITextView *comment = [[UITextView alloc] init];
comment.translatesAutoresizingMaskIntoConstraints = NO;
comment.layer.borderWidth = 1.0;
comment.layer.cornerRadius = 8;
comment.layer.borderColor = [UIColor lightGrayColor].CGColor;
comment.font = [UIFont systemFontOfSize:15];
[content addSubview:comment];
[NSLayoutConstraint activateConstraints:@[
[comment.topAnchor constraintEqualToAnchor:commentLabel.bottomAnchor constant:8],
[comment.leadingAnchor constraintEqualToAnchor:content.leadingAnchor constant:20],
[comment.trailingAnchor constraintEqualToAnchor:content.trailingAnchor constant:-20],
[comment.heightAnchor constraintEqualToConstant:100],
]];
self.commentField = comment;
lastView = comment;
// Image upload section
UILabel *uploadLabel = [self makeLabelWithText:@"*Image Upload" bold:YES size:15];
[content addSubview:uploadLabel];
[uploadLabel.topAnchor constraintEqualToAnchor:lastView.bottomAnchor constant:spacing].active = YES;
[uploadLabel.leadingAnchor constraintEqualToAnchor:forLabel.leadingAnchor].active = YES;
UIStackView *uploadRow = [[UIStackView alloc] init];
uploadRow.axis = UILayoutConstraintAxisHorizontal;
uploadRow.spacing = 12;
uploadRow.distribution = UIStackViewDistributionFillEqually;
uploadRow.translatesAutoresizingMaskIntoConstraints = NO;
[content addSubview:uploadRow];
[NSLayoutConstraint activateConstraints:@[
[uploadRow.topAnchor constraintEqualToAnchor:uploadLabel.bottomAnchor constant:8],
[uploadRow.leadingAnchor constraintEqualToAnchor:content.leadingAnchor constant:20],
[uploadRow.trailingAnchor constraintEqualToAnchor:content.trailingAnchor constant:-20],
[uploadRow.heightAnchor constraintEqualToConstant:90],
]];
for (int i = 0; i < 3; i++) {
UIButton *uploadBtn = [self makeUploadBox];
uploadBtn.tag = 1000 + i; // unique ID for each button
[uploadRow addArrangedSubview:uploadBtn];
}
lastView = uploadRow;
// Submit button
UIButton *submit = [UIButton buttonWithType:UIButtonTypeSystem];
submit.translatesAutoresizingMaskIntoConstraints = NO;
submit.backgroundColor = [UIColor colorWithRed:0/255.0 green:109/255.0 blue:141/255.0 alpha:1];
[submit setTitle:@"Submit" forState:UIControlStateNormal];
[submit setTitleColor:UIColor.whiteColor forState:UIControlStateNormal];
submit.titleLabel.font = [UIFont boldSystemFontOfSize:16];
submit.layer.cornerRadius = 8;
[submit addTarget:self action:@selector(handleSubmit) forControlEvents:UIControlEventTouchUpInside];
[content addSubview:submit];
[NSLayoutConstraint activateConstraints:@[
[submit.topAnchor constraintEqualToAnchor:lastView.bottomAnchor constant:spacing],
[submit.leadingAnchor constraintEqualToAnchor:content.leadingAnchor constant:20],
[submit.trailingAnchor constraintEqualToAnchor:content.trailingAnchor constant:-20],
[submit.heightAnchor constraintEqualToConstant:48],
]];
lastView = submit;
[content.bottomAnchor constraintEqualToAnchor:lastView.bottomAnchor constant:spacing].active = YES;
}
- (UILabel *)makeLabelWithText:(NSString *)text bold:(BOOL)bold size:(CGFloat)size {
UILabel *lbl = [[UILabel alloc] init];
lbl.translatesAutoresizingMaskIntoConstraints = NO;
lbl.text = text;
lbl.font = bold ? [UIFont boldSystemFontOfSize:size] : [UIFont systemFontOfSize:size];
lbl.textColor = [UIColor darkTextColor];
return lbl;
}
- (UIView *)makeRoundedBox {
UIView *box = [[UIView alloc] init];
box.translatesAutoresizingMaskIntoConstraints = NO;
box.layer.borderWidth = 1.0;
box.layer.borderColor = [UIColor lightGrayColor].CGColor;
box.layer.cornerRadius = 8;
return box;
}
- (UIButton *)makeDropdownWithPlaceholder:(NSString *)placeholder {
UIButton *btn = [UIButton buttonWithType:UIButtonTypeSystem];
btn.translatesAutoresizingMaskIntoConstraints = NO;
btn.layer.borderWidth = 1.0;
btn.layer.cornerRadius = 8;
btn.layer.borderColor = [UIColor lightGrayColor].CGColor;
btn.backgroundColor = [UIColor colorWithWhite:0.97 alpha:1];
// Title setup
[btn setTitle:placeholder forState:UIControlStateNormal];
[btn setTitleColor:[UIColor lightGrayColor] forState:UIControlStateNormal];
btn.titleLabel.font = [UIFont systemFontOfSize:15];
// Align text on the left
btn.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
btn.contentEdgeInsets = UIEdgeInsetsMake(0, 12, 0, 0);
// Add a dropdown chevron (SF Symbol)
UIImage *chevron = [UIImage systemImageNamed:@"chevron.down"];
UIImageView *arrow = [[UIImageView alloc] initWithImage:chevron];
arrow.translatesAutoresizingMaskIntoConstraints = NO;
arrow.tintColor = [UIColor grayColor];
[btn addSubview:arrow];
// Position arrow on right side
[NSLayoutConstraint activateConstraints:@[
[arrow.centerYAnchor constraintEqualToAnchor:btn.centerYAnchor],
[arrow.trailingAnchor constraintEqualToAnchor:btn.trailingAnchor constant:-12],
[arrow.widthAnchor constraintEqualToConstant:12],
[arrow.heightAnchor constraintEqualToConstant:8]
]];
// Bring title above arrow
btn.titleEdgeInsets = UIEdgeInsetsMake(0, 0, 0, 20);
// Add tap handler (for menu)
[btn addTarget:self action:@selector(handleDropdownTap:) forControlEvents:UIControlEventTouchUpInside];
return btn;
}
- (void)handleDropdownTap:(UIButton *)sender {
NSString *title = sender.titleLabel.text ?: @"Select";
UIAlertController *menu = [UIAlertController alertControllerWithTitle:title
message:@"Pick an option"
preferredStyle:UIAlertControllerStyleActionSheet];
NSArray *options = @[@"Option 1", @"Option 2", @"Option 3"];
for (NSString *opt in options) {
[menu addAction:[UIAlertAction actionWithTitle:opt
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * _Nonnull action) {
[sender setTitle:opt forState:UIControlStateNormal];
[sender setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
}]];
}
[menu addAction:[UIAlertAction actionWithTitle:@"Cancel"
style:UIAlertActionStyleCancel
handler:nil]];
[self presentViewController:menu animated:YES completion:nil];
}
- (UIButton *)makeUploadBox {
UIButton *upload = [UIButton buttonWithType:UIButtonTypeCustom]; // ✅ not System
upload.translatesAutoresizingMaskIntoConstraints = NO;
upload.layer.borderWidth = 1.0;
upload.layer.cornerRadius = 8;
upload.layer.borderColor = [UIColor lightGrayColor].CGColor;
[upload setTitle:@"Upload" forState:UIControlStateNormal];
[upload setTitleColor:[UIColor colorWithRed:0/255.0 green:109/255.0 blue:141/255.0 alpha:1] forState:UIControlStateNormal];
[upload setImage:[UIImage systemImageNamed:@"camera"] forState:UIControlStateNormal];
upload.tintColor = [UIColor colorWithRed:0/255.0 green:109/255.0 blue:141/255.0 alpha:1];
upload.imageEdgeInsets = UIEdgeInsetsMake(0, 0, 0, 8);
upload.titleLabel.font = [UIFont boldSystemFontOfSize:14];
[upload addTarget:self action:@selector(handleUploadTap:) forControlEvents:UIControlEventTouchUpInside];
return upload;
}
- (void)handleUploadTap:(UIButton *)sender {
NSInteger index = sender.tag - 1000;
UIAlertController *sheet = [UIAlertController alertControllerWithTitle:@"Upload Image"
message:nil
preferredStyle:UIAlertControllerStyleActionSheet];
[sheet addAction:[UIAlertAction actionWithTitle:@"Camera"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * _Nonnull action) {
[self openImagePickerForIndex:index source:UIImagePickerControllerSourceTypeCamera];
}]];
[sheet addAction:[UIAlertAction actionWithTitle:@"Gallery"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * _Nonnull action) {
[self openImagePickerForIndex:index source:UIImagePickerControllerSourceTypePhotoLibrary];
}]];
[sheet addAction:[UIAlertAction actionWithTitle:@"Cancel"
style:UIAlertActionStyleCancel
handler:nil]];
[self presentViewController:sheet animated:YES completion:nil];
}
- (void)openImagePickerForIndex:(NSInteger)index source:(UIImagePickerControllerSourceType)source {
if (![UIImagePickerController isSourceTypeAvailable:source]) return;
UIImagePickerController *picker = [[UIImagePickerController alloc] init];
picker.delegate = self;
picker.sourceType = source;
picker.view.tag = index; // store which slot this is for
[self presentViewController:picker animated:YES completion:nil];
}
#pragma mark - UIImagePickerControllerDelegate
- (void)imagePickerController:(UIImagePickerController *)picker
didFinishPickingMediaWithInfo:(NSDictionary<UIImagePickerControllerInfoKey,id> *)info {
UIImage *image = info[UIImagePickerControllerOriginalImage];
NSInteger index = picker.view.tag; // will be 0–2 after fix
// Store image
if (index < self.uploadedImages.count) {
self.uploadedImages[index] = image;
} else {
[self.uploadedImages addObject:image];
}
// Update UI
UIButton *button = [self.view viewWithTag:1000 + index];
if ([button isKindOfClass:[UIButton class]]) {
[button setTitle:nil forState:UIControlStateNormal];
[button setImage:image forState:UIControlStateNormal];
button.imageView.contentMode = UIViewContentModeScaleAspectFill;
button.imageView.clipsToBounds = YES;
button.layer.masksToBounds = YES;
button.contentHorizontalAlignment = UIControlContentHorizontalAlignmentFill;
button.contentVerticalAlignment = UIControlContentVerticalAlignmentFill;
button.imageEdgeInsets = UIEdgeInsetsZero;
[self addRemoveButtonToUpload:button atIndex:index];
}
[picker dismissViewControllerAnimated:YES completion:nil];
}
- (void)addRemoveButtonToUpload:(UIButton *)uploadBtn atIndex:(NSInteger)index {
// Remove existing remove button if any (avoid duplicates)
for (UIView *sub in uploadBtn.subviews) {
if (sub.tag == 9000) {
[sub removeFromSuperview];
}
}
// Create small X button
UIButton *removeBtn = [UIButton buttonWithType:UIButtonTypeCustom];
removeBtn.tag = 9000;
removeBtn.translatesAutoresizingMaskIntoConstraints = NO;
[removeBtn setImage:[UIImage systemImageNamed:@"xmark.circle.fill"] forState:UIControlStateNormal];
removeBtn.tintColor = [UIColor whiteColor];
removeBtn.backgroundColor = [UIColor colorWithWhite:0 alpha:0.4];
removeBtn.layer.cornerRadius = 10;
removeBtn.clipsToBounds = YES;
removeBtn.contentEdgeInsets = UIEdgeInsetsMake(4, 4, 4, 4);
removeBtn.accessibilityIdentifier = [NSString stringWithFormat:@"remove_%ld", (long)index];
[removeBtn addTarget:self action:@selector(handleRemoveImage:) forControlEvents:UIControlEventTouchUpInside];
[uploadBtn addSubview:removeBtn];
// Position in top-right corner
[NSLayoutConstraint activateConstraints:@[
[removeBtn.topAnchor constraintEqualToAnchor:uploadBtn.topAnchor constant:4],
[removeBtn.trailingAnchor constraintEqualToAnchor:uploadBtn.trailingAnchor constant:-4],
[removeBtn.widthAnchor constraintEqualToConstant:20],
[removeBtn.heightAnchor constraintEqualToConstant:20],
]];
}
- (void)handleRemoveImage:(UIButton *)sender {
UIView *uploadBtn = sender.superview;
if (![uploadBtn isKindOfClass:[UIButton class]]) return;
NSInteger index = ((UIButton *)uploadBtn).tag - 1000;
if (index < self.uploadedImages.count) {
[self.uploadedImages removeObjectAtIndex:index];
}
// Reset upload button
UIButton *button = (UIButton *)uploadBtn;
[button setImage:[UIImage systemImageNamed:@"camera"] forState:UIControlStateNormal];
[button setTitle:@"Upload" forState:UIControlStateNormal];
[button setTitleColor:[UIColor colorWithRed:0/255.0 green:109/255.0 blue:141/255.0 alpha:1] forState:UIControlStateNormal];
button.tintColor = [UIColor colorWithRed:0/255.0 green:109/255.0 blue:141/255.0 alpha:1];
button.imageView.contentMode = UIViewContentModeCenter;
button.contentHorizontalAlignment = UIControlContentHorizontalAlignmentCenter;
button.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
button.imageEdgeInsets = UIEdgeInsetsMake(0, 0, 0, 8);
// Remove the small "x" button
[sender removeFromSuperview];
}
}
- (void)handleReset {
- (void)handleReset {
...
@@ -177,6 +535,7 @@
...
@@ -177,6 +535,7 @@
style:UIAlertActionStyleDefault
style:UIAlertActionStyleDefault
handler:nil]];
handler:nil]];
[self presentViewController:alert animated:YES completion:nil];
[self presentViewController:alert animated:YES completion:nil];
[self.uploadedImages removeAllObjects];
}
}
...
...
framework/QmsPluginFramework/QmsPluginFramework/AddIssue/PlanViewController.mm
View file @
2ac4e245
...
@@ -12,6 +12,8 @@
...
@@ -12,6 +12,8 @@
@property (nonatomic, strong) UIScrollView *scrollView;
@property (nonatomic, strong) UIScrollView *scrollView;
@property (nonatomic, strong) UIImageView *planImageView;
@property (nonatomic, strong) UIImageView *planImageView;
@property (nonatomic, strong) NSString *selectedLocation;
@end
@end
@implementation PlanViewController
@implementation PlanViewController
...
@@ -170,11 +172,115 @@
...
@@ -170,11 +172,115 @@
}
}
- (void)handleNextScreen {
- (void)handleNextScreen {
NSLog(@"➡️ Test button tapped — navigating to AddIssueDetailsViewController");
[self showAddIssuePopup];
}
- (void)showAddIssuePopup {
NSLog(@"🟢 Showing Add Issue popup");
// Background dim view
UIView *overlay = [[UIView alloc] initWithFrame:self.view.bounds];
overlay.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.4];
overlay.alpha = 0.0;
overlay.tag = 999; // so we can find and remove it later
[self.view addSubview:overlay];
// Popup container
CGFloat safeBottom = self.view.safeAreaInsets.bottom;
CGFloat popupHeight = 180 + safeBottom;
UIView *popup = [[UIView alloc] initWithFrame:CGRectMake(0,
self.view.bounds.size.height,
self.view.bounds.size.width,
popupHeight)];
popup.backgroundColor = UIColor.whiteColor;
popup.layer.cornerRadius = 16;
popup.layer.maskedCorners = kCALayerMinXMinYCorner | kCALayerMaxXMinYCorner;
popup.clipsToBounds = YES;
popup.tag = 1000;
[self.view addSubview:popup];
// Label — “You have selected”
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(20, 20, popup.bounds.size.width - 40, 20)];
label.text = @"You have selected:";
label.textColor = [UIColor blackColor];
label.font = [UIFont systemFontOfSize:15 weight:UIFontWeightRegular];
[popup addSubview:label];
// Mock location name
UILabel *locationLabel = [[UILabel alloc] initWithFrame:CGRectMake(20, 50, popup.bounds.size.width - 40, 24)];
locationLabel.text = @"Building A - 2nd Floor";
self.selectedLocation = locationLabel.text;
locationLabel.textColor = [UIColor blackColor];
locationLabel.font = [UIFont boldSystemFontOfSize:17];
[popup addSubview:locationLabel];
// Buttons row
CGFloat buttonWidth = (self.view.bounds.size.width - 48) / 2;
CGFloat buttonY = popupHeight - 60 - safeBottom; // ✅ push up above safe area
UIButton *reselectButton = [UIButton buttonWithType:UIButtonTypeSystem];
reselectButton.frame = CGRectMake(16, buttonY, buttonWidth, 44);
[reselectButton setTitle:@"Reselect" forState:UIControlStateNormal];
[reselectButton setTitleColor:[UIColor colorWithRed:0/255.0 green:109/255.0 blue:141/255.0 alpha:1]
forState:UIControlStateNormal];
reselectButton.layer.borderWidth = 1.0;
reselectButton.layer.borderColor = [UIColor colorWithRed:0/255.0 green:109/255.0 blue:141/255.0 alpha:1].CGColor;
reselectButton.layer.cornerRadius = 8;
[reselectButton addTarget:self action:@selector(dismissAddIssuePopup)
forControlEvents:UIControlEventTouchUpInside];
[popup addSubview:reselectButton];
UIButton *nextButton = [UIButton buttonWithType:UIButtonTypeSystem];
nextButton.frame = CGRectMake(32 + buttonWidth, buttonY, buttonWidth, 44);
[nextButton setTitle:@"Next" forState:UIControlStateNormal];
[nextButton setTitleColor:UIColor.whiteColor forState:UIControlStateNormal];
nextButton.backgroundColor = [UIColor colorWithRed:0/255.0 green:109/255.0 blue:141/255.0 alpha:1];
nextButton.layer.cornerRadius = 8;
[nextButton addTarget:self action:@selector(handleConfirmAddIssue)
forControlEvents:UIControlEventTouchUpInside];
[popup addSubview:nextButton];
// Animate in
[UIView animateWithDuration:0.3 animations:^{
overlay.alpha = 1.0;
popup.frame = CGRectMake(0,
self.view.bounds.size.height - popupHeight,
self.view.bounds.size.width,
popupHeight);
}];
}
- (void)dismissAddIssuePopup {
NSLog(@"❌ Dismissing Add Issue popup");
UIView *overlay = [self.view viewWithTag:999];
UIView *popup = [self.view viewWithTag:1000];
[UIView animateWithDuration:0.25 animations:^{
overlay.alpha = 0.0;
popup.frame = CGRectMake(0,
self.view.bounds.size.height,
popup.bounds.size.width,
popup.bounds.size.height);
} completion:^(BOOL finished) {
[overlay removeFromSuperview];
[popup removeFromSuperview];
}];
}
- (void)handleConfirmAddIssue {
NSLog(@"✅ Confirm Add Issue pressed");
// Dismiss popup first
[self dismissAddIssuePopup];
AddIssueDetailsViewController *nextVC = [[AddIssueDetailsViewController alloc] init];
// Then open AddIssueDetailsViewController after a short delay
nextVC.modalPresentationStyle = UIModalPresentationFullScreen;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.25 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self presentViewController:nextVC animated:YES completion:nil];
AddIssueDetailsViewController *nextVC = [[AddIssueDetailsViewController alloc] init];
nextVC.selectedLocationName = self.selectedLocation; // ✅ Pass it here
nextVC.modalPresentationStyle = UIModalPresentationFullScreen;
[self presentViewController:nextVC animated:YES completion:nil];
});
}
}
#pragma mark - Actions
#pragma mark - Actions
...
...
framework/QmsPluginFramework/QmsPluginFramework/Dashboard/DashboardAPIClient.h
0 → 100644
View file @
2ac4e245
// DashboardAPIClient.h
#import <Foundation/Foundation.h>
@interface
DashboardAPIClient
:
NSObject
+
(
void
)
fetchDashboardSummary
:(
void
(
^
)(
NSDictionary
*
data
))
completion
;
+
(
void
)
fetchAnnouncements
:(
void
(
^
)(
NSArray
*
data
))
completion
;
+
(
void
)
fetchAppointments
:(
void
(
^
)(
NSArray
*
data
))
completion
;
+
(
void
)
fetchIssues
:(
void
(
^
)(
NSArray
*
data
))
completion
;
+
(
void
)
fetchHeaderInfo
:(
void
(
^
)(
NSDictionary
*
data
))
completion
;
@end
framework/QmsPluginFramework/QmsPluginFramework/Dashboard/DashboardAPIClient.mm
0 → 100644
View file @
2ac4e245
// DashbaordAPIClient.mm
#import "DashboardAPIClient.h"
@implementation DashboardAPIClient
+ (void)fetchDashboardSummary:(void (^)(NSDictionary *))completion {
NSURL *url = [NSURL URLWithString:@"https://jsonplaceholder.typicode.com/users/1"];
[[[NSURLSession sharedSession] dataTaskWithURL:url
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (error) { completion(nil); return; }
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
dispatch_async(dispatch_get_main_queue(), ^{
completion(json);
});
}] resume];
}
+ (void)fetchAnnouncements:(void (^)(NSArray *))completion {
NSURL *url = [NSURL URLWithString:@"https://jsonplaceholder.typicode.com/posts?_limit=5"];
[[[NSURLSession sharedSession] dataTaskWithURL:url
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (error) { completion(nil); return; }
NSArray *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
dispatch_async(dispatch_get_main_queue(), ^{
completion(json);
});
}] resume];
}
+ (void)fetchAppointments:(void (^)(NSArray *))completion {
NSURL *url = [NSURL URLWithString:@"https://jsonplaceholder.typicode.com/todos?_limit=5"];
[[[NSURLSession sharedSession] dataTaskWithURL:url
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (error) { completion(nil); return; }
NSArray *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
dispatch_async(dispatch_get_main_queue(), ^{
completion(json);
});
}] resume];
}
+ (void)fetchIssues:(void (^)(NSArray *))completion {
NSURL *url = [NSURL URLWithString:@"https://jsonplaceholder.typicode.com/comments?_limit=5"];
[[[NSURLSession sharedSession] dataTaskWithURL:url
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (error) { completion(nil); return; }
NSArray *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
dispatch_async(dispatch_get_main_queue(), ^{
completion(json);
});
}] resume];
}
+ (void)fetchHeaderInfo:(void (^)(NSDictionary *data))completion {
NSURL *url = [NSURL URLWithString:@"https://jsonplaceholder.typicode.com/photos/1"];
NSURLSessionDataTask *task = [[NSURLSession sharedSession] dataTaskWithURL:url
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (error) {
NSLog(@"❌ Header info fetch failed: %@", error);
if (completion) completion(@{});
return;
}
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
if (completion) completion(json);
}];
[task resume];
}
@end
framework/QmsPluginFramework/QmsPluginFramework/Dashboard/DashboardHeaderView.mm
View file @
2ac4e245
//DashboardHeaderView.mm
#import "DashboardHeaderView.h"
#import "DashboardHeaderView.h"
@implementation DashboardHeaderView
@implementation DashboardHeaderView
...
...
framework/QmsPluginFramework/QmsPluginFramework/Dashboard/DashboardViewController.mm
View file @
2ac4e245
// DashboardViewController.mm
#import "DashboardViewController.h"
#import "DashboardViewController.h"
#import "DashboardHeaderView.h"
#import "DashboardHeaderView.h"
#import <CoreGraphics/CoreGraphics.h>
#import <CoreGraphics/CoreGraphics.h>
#import "PlanViewController.h"
#import "PlanViewController.h"
#import "DashboardAPIClient.h"
@implementation DashboardViewController {
@implementation DashboardViewController {
DashboardHeaderView *_headerView;
DashboardHeaderView *_headerView;
UIScrollView *_scrollView;
UIScrollView *_scrollView;
UIView *_contentContainer;
UIView *_contentContainer
, *_inspectionBanner
;
NSMutableDictionary *_sectionViews;
NSMutableDictionary *_sectionViews;
CGFloat _currentY;
}
}
- (void)viewDidLoad {
- (void)viewDidLoad {
...
@@ -25,6 +28,7 @@
...
@@ -25,6 +28,7 @@
#pragma mark - Layout
#pragma mark - Layout
- (void)viewDidLayoutSubviews {
- (void)viewDidLayoutSubviews {
NSLog(@"Header frame: %@", NSStringFromCGRect(_headerView.frame));
[super viewDidLayoutSubviews];
[super viewDidLayoutSubviews];
UIView *footerView = self.view.subviews.lastObject;
UIView *footerView = self.view.subviews.lastObject;
CGFloat footerHeight = 80.0;
CGFloat footerHeight = 80.0;
...
@@ -159,88 +163,273 @@
...
@@ -159,88 +163,273 @@
- (void)setupScrollContent {
- (void)setupScrollContent {
CGFloat topOffset = 220.0;
CGFloat topOffset = 220.0;
_scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, topOffset, self.view.bounds.size.width, self.view.bounds.size.height - topOffset)];
_scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(
0,
topOffset,
self.view.bounds.size.width,
self.view.bounds.size.height - topOffset
)];
_scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
_scrollView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
_scrollView.backgroundColor = [UIColor clearColor];
_scrollView.backgroundColor = [UIColor clearColor];
// 🩹 Add dynamic bottom padding so last card doesn’t clip under footer
CGFloat footerHeight = 80.0 + self.view.safeAreaInsets.bottom;
_scrollView.contentInset = UIEdgeInsetsMake(0, 0, footerHeight, 0);
_scrollView.scrollIndicatorInsets = _scrollView.contentInset;
[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)];
_contentContainer.autoresizingMask = UIViewAutoresizingFlexibleWidth;
_contentContainer.autoresizingMask = UIViewAutoresizingFlexibleWidth;
[_scrollView addSubview:_contentContainer];
[_scrollView addSubview:_contentContainer];
_currentY = 16.0;
[self setupInspectionChecklistBanner];
if (_currentY == 0) {
_currentY = 16.0;
}
}
CGFloat y = 20;
#pragma mark - Inspection Checklist Banner
// Add 3 fixed section titles, store them for later population
- (void)setupInspectionChecklistBanner {
NSArray *sectionNames = @[ @"Project Update", @"Appointments", @"Issue Updates" ];
BOOL blnContactlessHandover = NO; // mock GLOBALS.BLN_CONTACTLESS_HANDOVER == 1
for (NSString *name in sectionNames) {
BOOL blnInspChecklist = NO; // mock self.bln_InspChecklist
UILabel *header = [[UILabel alloc] initWithFrame:CGRectMake(16, y, self.view.bounds.size.width - 32, 24)];
NSString *handoverStatus = @"Pending"; // mock self.strContactlessHandoverStatus
header.text = name;
NSInteger daysRemaining = 3; // mock self.strDays
header.font = [UIFont boldSystemFontOfSize:18];
[_contentContainer addSubview:header];
y += 30;
UIView *sectionContainer = [[UIView alloc] initWithFrame:CGRectMake(0, y, self.view.bounds.size.width, 0)];
// ✅ Condition check
[_contentContainer addSubview:sectionContainer];
if (!(blnContactlessHandover && blnInspChecklist && [handoverStatus isEqualToString:@"Pending"])) {
_sectionViews[name] = sectionContainer;
NSLog(@"ℹ️ No inspection checklist banner required");
return;
}
y += 20; // spacing before next section
// Banner container
_inspectionBanner = [[UIView alloc] initWithFrame:CGRectMake(16, 16, self.view.bounds.size.width - 32, 90)];
_inspectionBanner.backgroundColor = [UIColor colorWithRed:1.0 green:0.39 blue:0.39 alpha:1.0]; // #FF6464
_inspectionBanner.layer.cornerRadius = 20;
_inspectionBanner.clipsToBounds = YES;
// Tap gesture (simulate button press)
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleInspectionBannerTap)];
[_inspectionBanner addGestureRecognizer:tap];
// Title label
UILabel *titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(16, 16, _inspectionBanner.bounds.size.width - 80, 24)];
titleLabel.text = @"Submit Unit Inspection Checklist";
titleLabel.font = [UIFont boldSystemFontOfSize:16];
titleLabel.textColor = UIColor.whiteColor;
[_inspectionBanner addSubview:titleLabel];
// Right arrow icon
UIImageView *arrow = [[UIImageView alloc] initWithImage:[UIImage systemImageNamed:@"chevron.right"]];
arrow.tintColor = UIColor.whiteColor;
arrow.frame = CGRectMake(_inspectionBanner.bounds.size.width - 36, (_inspectionBanner.bounds.size.height - 24)/2, 20, 24);
arrow.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin;
[_inspectionBanner addSubview:arrow];
// Days remaining pill (optional)
if (daysRemaining > 0) {
UIView *pill = [[UIView alloc] initWithFrame:CGRectMake(16, CGRectGetMaxY(titleLabel.frame) + 6, 170, 28)];
pill.backgroundColor = [UIColor colorWithWhite:0 alpha:0.4];
pill.layer.cornerRadius = 14;
pill.clipsToBounds = YES;
UIImageView *calendarIcon = [[UIImageView alloc] initWithImage:[UIImage systemImageNamed:@"calendar"]];
calendarIcon.tintColor = UIColor.whiteColor;
calendarIcon.frame = CGRectMake(8, 4, 20, 20);
[pill addSubview:calendarIcon];
UILabel *daysLabel = [[UILabel alloc] initWithFrame:CGRectMake(34, 4, 130, 20)];
daysLabel.text = [NSString stringWithFormat:@"%ld days remaining", (long)daysRemaining];
daysLabel.textColor = UIColor.whiteColor;
daysLabel.font = [UIFont boldSystemFontOfSize:13];
[pill addSubview:daysLabel];
[_inspectionBanner addSubview:pill];
}
}
_contentContainer.frame = CGRectMake(0, 0, _scrollView.bounds.size.width, y);
[_contentContainer addSubview:_inspectionBanner];
_scrollView.contentSize = CGSizeMake(_scrollView.bounds.size.width, y);
_currentY = CGRectGetMaxY(_inspectionBanner.frame) + 16.0;
}
- (void)handleInspectionBannerTap {
NSLog(@"🟥 Inspection checklist banner tapped");
// In future: open WebView screen or perform navigation
[UIView animateWithDuration:0.1 animations:^{
_inspectionBanner.alpha = 0.6;
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.1 animations:^{
_inspectionBanner.alpha = 1.0;
}];
}];
}
}
#pragma mark - Networking
#pragma mark - Networking
- (void)fetchDashboardData {
- (void)fetchDashboardData {
NSURL *url = [NSURL URLWithString:@"https://jsonplaceholder.typicode.com/posts?_limit=9"];
__weak typeof(self) weakSelf = self;
__weak typeof(self) weakSelf = self;
[[[NSURLSession sharedSession] dataTaskWithURL:url
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
[DashboardAPIClient fetchDashboardSummary:^(NSDictionary *data) {
if (error) return;
__strong typeof(weakSelf) strongSelf = weakSelf;
NSArray *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSLog(@"🏗 Dashboard Summary: %@", data);
dispatch_async(dispatch_get_main_queue(), ^{
// In the future: update header info (project/unit name, etc.)
[weakSelf populateSectionsWithData:json];
[strongSelf->_headerView configureWithProjectName:data[@"name"]
});
unitName:data[@"username"]
}] resume];
backgroundImage:[UIImage imageNamed:@"header_bg"]
}
overlayImage:[UIImage imageNamed:@"overlay"]];
}];
- (void)populateSectionsWithData:(NSArray *)data {
NSArray *sectionKeys = @[ @"Project Update", @"Appointments", @"Issue Updates" ];
[DashboardAPIClient fetchHeaderInfo:^(NSDictionary *data) {
int itemsPerSection = (int)ceil(data.count / 3.0);
__strong typeof(weakSelf) strongSelf = weakSelf;
if (!strongSelf) return;
CGFloat sectionBottomY = 0;
for (int i = 0; i < sectionKeys.count; i++) {
NSLog(@"🖼 Header info: %@", data);
NSString *key = sectionKeys[i];
UIView *sectionContainer = _sectionViews[key];
NSString *projectName = data[@"title"] ?: @"Project A";
CGFloat y = 0;
NSString *unitName = @"Demo Unit 3A";
NSString *imageURL = data[@"url"];
NSArray *subset = [data subarrayWithRange:NSMakeRange(i * itemsPerSection,
MIN(itemsPerSection, data.count - i * itemsPerSection))];
NSURL *url = [NSURL URLWithString:imageURL];
NSURLSessionDataTask *task = [[NSURLSession sharedSession] dataTaskWithURL:url
for (NSDictionary *item in subset) {
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
UIView *card = [[UIView alloc] initWithFrame:CGRectMake(16, y, self.view.bounds.size.width - 32, 100)];
if (error) {
card.backgroundColor = [UIColor whiteColor];
NSLog(@"❌ Failed to download image: %@", error.localizedDescription);
card.layer.cornerRadius = 8;
return;
card.layer.shadowColor = [UIColor colorWithWhite:0 alpha:0.1].CGColor;
}
card.layer.shadowOpacity = 0.8;
UIImage *image = [UIImage imageWithData:data];
card.layer.shadowOffset = CGSizeMake(0, 1);
if (!image) return;
UILabel *body = [[UILabel alloc] initWithFrame:CGRectMake(12, 12, card.bounds.size.width - 24, 80)];
dispatch_async(dispatch_get_main_queue(), ^{
body.text = item[@"title"];
[strongSelf->_headerView configureWithProjectName:projectName
body.numberOfLines = 2;
unitName:unitName
body.font = [UIFont systemFontOfSize:15];
backgroundImage:image
[card addSubview:body];
overlayImage:[UIImage imageNamed:@"overlay"]];
[sectionContainer addSubview:card];
});
}];
[task resume];
NSLog(@"🖼 Header image URL: %@", imageURL);
}];
[DashboardAPIClient fetchAnnouncements:^(NSArray *data) {
__strong typeof(weakSelf) strongSelf = weakSelf;
NSLog(@"📢 Announcements: %@", data);
[strongSelf populateSection:@"Project Update" withData:data];
}];
[DashboardAPIClient fetchAppointments:^(NSArray *data) {
__strong typeof(weakSelf) strongSelf = weakSelf;
NSLog(@"📅 Appointments: %@", data);
[strongSelf populateSection:@"Appointments" withData:data];
}];
[DashboardAPIClient fetchIssues:^(NSArray *data) {
__strong typeof(weakSelf) strongSelf = weakSelf;
NSLog(@"🪲 Issues: %@", data);
[strongSelf populateSection:@"Issue Updates" withData:data];
}];
}
- (UIImage *)downloadImageFrom:(NSString *)urlString {
NSData *data = [NSData dataWithContentsOfURL:[NSURL URLWithString:urlString]];
return data ? [UIImage imageWithData:data] : [UIImage imageNamed:@"header_bg"];
}
- (void)populateSection:(NSString *)title withData:(NSArray *)data {
CGFloat y = _currentY;
// --- Section Header ---
UILabel *header = [[UILabel alloc] initWithFrame:CGRectMake(16, y, self.view.bounds.size.width - 32, 24)];
header.text = title;
header.font = [UIFont boldSystemFontOfSize:18];
[_contentContainer addSubview:header];
y += 34;
// --- Special handling for Project Update ---
if ([title isEqualToString:@"Project Update"]) {
UIScrollView *hScroll = [[UIScrollView alloc] initWithFrame:CGRectMake(0, y, self.view.bounds.size.width, 140)];
hScroll.showsHorizontalScrollIndicator = NO;
hScroll.autoresizingMask = UIViewAutoresizingFlexibleWidth;
CGFloat x = 16;
for (NSDictionary *item in data) {
UIView *card = [self createCardWithText:item[@"title"] ?: @"No title"
frame:CGRectMake(x, 0, 220, 120)];
[hScroll addSubview:card];
x += 236; // card width + spacing
}
hScroll.contentSize = CGSizeMake(x, 140);
[_contentContainer addSubview:hScroll];
y += 150; // spacing after horizontal list
} else {
// --- Vertical cards for all other sections ---
for (NSDictionary *item in data) {
UIView *card = [self createCardWithText:item[@"title"] ?: @"No title"
frame:CGRectMake(16, y, self.view.bounds.size.width - 32, 100)];
[_contentContainer addSubview:card];
y += 120;
y += 120;
}
}
sectionContainer.frame = CGRectMake(0, sectionContainer.frame.origin.y, self.view.bounds.size.width, y);
y += 20; // extra bottom padding between sections
sectionBottomY = CGRectGetMaxY(sectionContainer.frame) + 20;
}
}
_contentContainer.frame = CGRectMake(0, 0, _scrollView.bounds.size.width, sectionBottomY);
_currentY = y;
_scrollView.contentSize = CGSizeMake(_scrollView.bounds.size.width, sectionBottomY);
_contentContainer.frame = CGRectMake(0, 0, _scrollView.bounds.size.width, _currentY);
_scrollView.contentSize = CGSizeMake(_scrollView.bounds.size.width, _currentY);
}
#pragma mark - Card Factory with Tap Animation
- (UIView *)createCardWithText:(NSString *)text frame:(CGRect)frame {
UIView *card = [[UIView alloc] initWithFrame:frame];
card.backgroundColor = [UIColor whiteColor];
card.layer.cornerRadius = 8;
card.layer.shadowColor = [UIColor colorWithWhite:0 alpha:0.1].CGColor;
card.layer.shadowOpacity = 0.8;
card.layer.shadowOffset = CGSizeMake(0, 1);
card.userInteractionEnabled = YES;
UILabel *body = [[UILabel alloc] initWithFrame:CGRectMake(12, 12, frame.size.width - 24, frame.size.height - 24)];
body.text = text;
body.numberOfLines = 2;
body.font = [UIFont systemFontOfSize:15];
[card addSubview:body];
// Add tap gesture with animation
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(cardTapped:)];
[card addGestureRecognizer:tap];
return card;
}
#pragma mark - Tap Handlers
- (void)sectionHeaderTapped:(UITapGestureRecognizer *)gesture {
UILabel *header = (UILabel *)gesture.view;
[self animateTapForView:header];
NSLog(@"📘 Section tapped: %@", header.text);
}
- (void)cardTapped:(UITapGestureRecognizer *)gesture {
UIView *card = gesture.view;
[self animateTapForView:card];
NSLog(@"🟩 Card tapped: %@", ((UILabel *)card.subviews.firstObject).text);
}
- (void)animateTapForView:(UIView *)view {
[UIView animateWithDuration:0.1 animations:^{
view.transform = CGAffineTransformMakeScale(0.96, 0.96);
view.alpha = 0.8;
} completion:^(BOOL finished) {
[UIView animateWithDuration:0.1 animations:^{
view.transform = CGAffineTransformIdentity;
view.alpha = 1.0;
}];
}];
}
}
@end
@end
ios/QmsPluginFramework.xcframework/ios-arm64/QmsPluginFramework.framework/QmsPluginFramework
View file @
2ac4e245
No preview for this file type
ios/QmsPluginFramework.xcframework/ios-arm64_x86_64-simulator/QmsPluginFramework.framework/QmsPluginFramework
View file @
2ac4e245
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