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

AddIssueDetails UI update

parent abcdd3b4
...@@ -8,32 +8,32 @@ ...@@ -8,32 +8,32 @@
<key>BinaryPath</key> <key>BinaryPath</key>
<string>QmsPluginFramework.framework/QmsPluginFramework</string> <string>QmsPluginFramework.framework/QmsPluginFramework</string>
<key>LibraryIdentifier</key> <key>LibraryIdentifier</key>
<string>ios-arm64</string> <string>ios-arm64_x86_64-simulator</string>
<key>LibraryPath</key> <key>LibraryPath</key>
<string>QmsPluginFramework.framework</string> <string>QmsPluginFramework.framework</string>
<key>SupportedArchitectures</key> <key>SupportedArchitectures</key>
<array> <array>
<string>arm64</string> <string>arm64</string>
<string>x86_64</string>
</array> </array>
<key>SupportedPlatform</key> <key>SupportedPlatform</key>
<string>ios</string> <string>ios</string>
<key>SupportedPlatformVariant</key>
<string>simulator</string>
</dict> </dict>
<dict> <dict>
<key>BinaryPath</key> <key>BinaryPath</key>
<string>QmsPluginFramework.framework/QmsPluginFramework</string> <string>QmsPluginFramework.framework/QmsPluginFramework</string>
<key>LibraryIdentifier</key> <key>LibraryIdentifier</key>
<string>ios-arm64_x86_64-simulator</string> <string>ios-arm64</string>
<key>LibraryPath</key> <key>LibraryPath</key>
<string>QmsPluginFramework.framework</string> <string>QmsPluginFramework.framework</string>
<key>SupportedArchitectures</key> <key>SupportedArchitectures</key>
<array> <array>
<string>arm64</string> <string>arm64</string>
<string>x86_64</string>
</array> </array>
<key>SupportedPlatform</key> <key>SupportedPlatform</key>
<string>ios</string> <string>ios</string>
<key>SupportedPlatformVariant</key>
<string>simulator</string>
</dict> </dict>
</array> </array>
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
2F99420D2EA9C5F000622199 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2F99420C2EA9C5F000622199 /* QuartzCore.framework */; }; 2F99420D2EA9C5F000622199 /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2F99420C2EA9C5F000622199 /* QuartzCore.framework */; };
2FAEF09A2E8B7BC80086EDA3 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2FAEF0992E8B7BC80086EDA3 /* UIKit.framework */; }; 2FAEF09A2E8B7BC80086EDA3 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2FAEF0992E8B7BC80086EDA3 /* UIKit.framework */; };
2FAEF09C2E8B7BD40086EDA3 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2FAEF09B2E8B7BD40086EDA3 /* Foundation.framework */; }; 2FAEF09C2E8B7BD40086EDA3 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2FAEF09B2E8B7BD40086EDA3 /* Foundation.framework */; };
2FB2922A2EAB04F400D9F164 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2FB292292EAB04F400D9F164 /* AVFoundation.framework */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
...@@ -19,6 +20,7 @@ ...@@ -19,6 +20,7 @@
2F99420C2EA9C5F000622199 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.0.sdk/System/Library/Frameworks/QuartzCore.framework; sourceTree = DEVELOPER_DIR; }; 2F99420C2EA9C5F000622199 /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.0.sdk/System/Library/Frameworks/QuartzCore.framework; sourceTree = DEVELOPER_DIR; };
2FAEF0992E8B7BC80086EDA3 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.0.sdk/System/iOSSupport/System/Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; }; 2FAEF0992E8B7BC80086EDA3 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.0.sdk/System/iOSSupport/System/Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; };
2FAEF09B2E8B7BD40086EDA3 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.0.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; 2FAEF09B2E8B7BD40086EDA3 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.0.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; };
2FB292292EAB04F400D9F164 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.0.sdk/System/Library/Frameworks/AVFoundation.framework; sourceTree = DEVELOPER_DIR; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ /* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
...@@ -48,6 +50,7 @@ ...@@ -48,6 +50,7 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
2FB2922A2EAB04F400D9F164 /* AVFoundation.framework in Frameworks */,
2F99420D2EA9C5F000622199 /* QuartzCore.framework in Frameworks */, 2F99420D2EA9C5F000622199 /* QuartzCore.framework in Frameworks */,
2F8D09092E9605E200C764FF /* CoreGraphics.framework in Frameworks */, 2F8D09092E9605E200C764FF /* CoreGraphics.framework in Frameworks */,
2FAEF09C2E8B7BD40086EDA3 /* Foundation.framework in Frameworks */, 2FAEF09C2E8B7BD40086EDA3 /* Foundation.framework in Frameworks */,
...@@ -78,6 +81,7 @@ ...@@ -78,6 +81,7 @@
2FAEF0982E8B7BC80086EDA3 /* Frameworks */ = { 2FAEF0982E8B7BC80086EDA3 /* Frameworks */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
2FB292292EAB04F400D9F164 /* AVFoundation.framework */,
2F99420C2EA9C5F000622199 /* QuartzCore.framework */, 2F99420C2EA9C5F000622199 /* QuartzCore.framework */,
2F8D09082E9605E200C764FF /* CoreGraphics.framework */, 2F8D09082E9605E200C764FF /* CoreGraphics.framework */,
2FAEF09B2E8B7BD40086EDA3 /* Foundation.framework */, 2FAEF09B2E8B7BD40086EDA3 /* Foundation.framework */,
......
...@@ -7,4 +7,12 @@ ...@@ -7,4 +7,12 @@
@property (nonatomic, strong) NSString *selectedLocationName; @property (nonatomic, strong) NSString *selectedLocationName;
@property (nonatomic, strong) NSMutableArray<UIImage *> *uploadedImages; @property (nonatomic, strong) NSMutableArray<UIImage *> *uploadedImages;
@property (nonatomic, strong) NSString *planID;
@property (nonatomic, strong) NSString *locationID;
@property (nonatomic, strong) NSDictionary *locationData;
@property (nonatomic, strong) NSString *defectMatrix;
@property (nonatomic, strong) NSString *projectCode;
@property (nonatomic, strong) NSString *projectName;
@end @end
...@@ -7,6 +7,16 @@ ...@@ -7,6 +7,16 @@
@property (nonatomic, strong) UITextView *commentField; @property (nonatomic, strong) UITextView *commentField;
@property (nonatomic, strong) UIButton *submitButton; @property (nonatomic, strong) UIButton *submitButton;
@property (nonatomic, strong) UIButton *resetButton; @property (nonatomic, strong) UIButton *resetButton;
@property (nonatomic, strong) NSArray *categories;
@property (nonatomic, strong) NSArray *types;
@property (nonatomic, strong) NSArray *issues;
@property (nonatomic, weak) UIButton *typeButton;
@property (nonatomic, weak) UIButton *issueButton;
@property (nonatomic, copy) NSString *selectedTypeName;
@end @end
@implementation AddIssueDetailsViewController @implementation AddIssueDetailsViewController
...@@ -15,6 +25,35 @@ ...@@ -15,6 +25,35 @@
[super viewDidLoad]; [super viewDidLoad];
self.view.backgroundColor = UIColor.whiteColor; self.view.backgroundColor = UIColor.whiteColor;
NSLog(@"📦 Data received in AddIssueDetailsViewController:\n planID: %@\n locationID: %@\n selectedLocationName: %@\n defectMatrix: %@\n locationData: %@",
self.planID,
self.locationID,
self.selectedLocationName,
self.defectMatrix,
self.locationData);
NSDictionary *locData = self.locationData;
if ([locData isKindOfClass:[NSDictionary class]]) {
self.categories = locData[@"category"];
// Extract all types under all categories (for now, assuming one category)
if (self.categories.count > 0) {
NSDictionary *firstCat = self.categories.firstObject;
self.types = firstCat[@"type"];
}
NSLog(@"✅ Parsed %lu categories, %lu types", (unsigned long)self.categories.count, (unsigned long)self.types.count);
}
if (self.categories.count > 0) {
NSDictionary *firstCat = self.categories.firstObject;
NSString *catName = firstCat[@"cat_name"];
UIButton *catBtn = [self.view viewWithTag:2000];
if ([catBtn isKindOfClass:[UIButton class]] && catName) {
[catBtn setTitle:catName forState:UIControlStateNormal];
[catBtn setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
}
}
// Create all views (don’t position them yet) // Create all views (don’t position them yet)
[self createHeader]; [self createHeader];
[self createForm]; [self createForm];
...@@ -163,7 +202,20 @@ ...@@ -163,7 +202,20 @@
[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;
UILabel *unitLabel = [self makeLabelWithText:@"Ara E Residence Dev, C-L(2)" bold:YES size:16]; // 🧩 Build dynamic text safely
NSString *unitText = @"";
if (self.projectName && self.projectCode) {
unitText = [NSString stringWithFormat:@"%@, %@", self.projectName, self.projectCode];
} else if (self.projectName) {
unitText = self.projectName;
} else if (self.projectCode) {
unitText = self.projectCode;
} else {
unitText = @"-"; // fallback if both missing
}
// 🏷 Create the label
UILabel *unitLabel = [self makeLabelWithText:unitText bold:YES size:16];
[content addSubview:unitLabel]; [content addSubview:unitLabel];
[unitLabel.topAnchor constraintEqualToAnchor:forLabel.bottomAnchor constant:4].active = YES; [unitLabel.topAnchor constraintEqualToAnchor:forLabel.bottomAnchor constant:4].active = YES;
[unitLabel.leadingAnchor constraintEqualToAnchor:forLabel.leadingAnchor].active = YES; [unitLabel.leadingAnchor constraintEqualToAnchor:forLabel.leadingAnchor].active = YES;
...@@ -184,6 +236,7 @@ ...@@ -184,6 +236,7 @@
[locationContainer.heightAnchor constraintEqualToConstant:44], [locationContainer.heightAnchor constraintEqualToConstant:44],
]]; ]];
// === 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";
...@@ -192,33 +245,46 @@ ...@@ -192,33 +245,46 @@
[locationContainer addSubview:locationField]; [locationContainer addSubview:locationField];
self.locationField = locationField; self.locationField = locationField;
[NSLayoutConstraint activateConstraints:@[
[locationField.leadingAnchor constraintEqualToAnchor:locationContainer.leadingAnchor constant:8],
[locationField.trailingAnchor constraintEqualToAnchor:locationContainer.trailingAnchor constant:-8],
[locationField.centerYAnchor constraintEqualToAnchor:locationContainer.centerYAnchor],
]];
// === Reselect Button (below the container, left-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] 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.titleLabel.font = [UIFont boldSystemFontOfSize:14];
[reselectBtn addTarget:self action:@selector(dismissSelf) forControlEvents:UIControlEventTouchUpInside]; [reselectBtn addTarget:self action:@selector(dismissSelf) forControlEvents:UIControlEventTouchUpInside];
[locationContainer addSubview:reselectBtn]; [content addSubview:reselectBtn];
[NSLayoutConstraint activateConstraints:@[ [NSLayoutConstraint activateConstraints:@[
[locationField.leadingAnchor constraintEqualToAnchor:locationContainer.leadingAnchor constant:8], [reselectBtn.topAnchor constraintEqualToAnchor:locationContainer.bottomAnchor constant:4],
[locationField.centerYAnchor constraintEqualToAnchor:locationContainer.centerYAnchor], [reselectBtn.leadingAnchor constraintEqualToAnchor:locationContainer.leadingAnchor constant:4],
[reselectBtn.trailingAnchor constraintEqualToAnchor:locationContainer.trailingAnchor constant:-4],
[reselectBtn.centerYAnchor constraintEqualToAnchor:locationContainer.centerYAnchor],
[locationField.trailingAnchor constraintEqualToAnchor:reselectBtn.leadingAnchor constant:-12],
]]; ]];
lastView = locationContainer; lastView = locationContainer;
// Category, Type, Issue — dropdown style // Category, Type, Issue — dropdown style
NSArray *titles = @[@"*Category", @"*Type", @"*Issue"]; NSArray *titles = @[@"*Category", @"*Type", @"*Issue"];
NSMutableArray *dropdowns = [NSMutableArray array];
for (NSString *t in titles) { for (int i = 0; i < titles.count; i++) {
UILabel *lbl = [self makeLabelWithText:t bold:YES size:15]; NSString *t = titles[i];
// Label for field title
UILabel *lbl = [self makeLabelWithText:t bold:NO size:13];
[content addSubview:lbl]; [content addSubview:lbl];
[lbl.topAnchor constraintEqualToAnchor:lastView.bottomAnchor constant:spacing].active = YES; [NSLayoutConstraint activateConstraints:@[
[lbl.leadingAnchor constraintEqualToAnchor:forLabel.leadingAnchor].active = YES; [lbl.topAnchor constraintEqualToAnchor:(lastView ? lastView.bottomAnchor : content.topAnchor) constant:(lastView ? 24 : 0)],
[lbl.leadingAnchor constraintEqualToAnchor:content.leadingAnchor constant:20],
[lbl.trailingAnchor constraintEqualToAnchor:content.trailingAnchor constant:-20],
]];
// Create dropdown button
UIButton *dropdown = [self makeDropdownWithPlaceholder:[NSString stringWithFormat:@"- Select %@ -", [t componentsSeparatedByString:@" "].lastObject]]; UIButton *dropdown = [self makeDropdownWithPlaceholder:[NSString stringWithFormat:@"- Select %@ -", [t componentsSeparatedByString:@" "].lastObject]];
dropdown.tag = 2000 + i;
[content addSubview:dropdown]; [content addSubview:dropdown];
[NSLayoutConstraint activateConstraints:@[ [NSLayoutConstraint activateConstraints:@[
[dropdown.topAnchor constraintEqualToAnchor:lbl.bottomAnchor constant:8], [dropdown.topAnchor constraintEqualToAnchor:lbl.bottomAnchor constant:8],
...@@ -226,7 +292,28 @@ ...@@ -226,7 +292,28 @@
[dropdown.trailingAnchor constraintEqualToAnchor:content.trailingAnchor constant:-20], [dropdown.trailingAnchor constraintEqualToAnchor:content.trailingAnchor constant:-20],
[dropdown.heightAnchor constraintEqualToConstant:44], [dropdown.heightAnchor constraintEqualToConstant:44],
]]; ]];
[dropdowns addObject:dropdown];
// Keep references for Type & Issue
if (i == 1) self.typeButton = dropdown;
if (i == 2) self.issueButton = dropdown;
// === Category Auto-Load + Disable Interaction ===
if (i == 0) {
NSArray *categories = self.locationData[@"category"];
if (categories.count > 0) {
NSDictionary *firstCategory = categories.firstObject;
NSString *categoryName = firstCategory[@"cat_name"] ?: @"";
[dropdown setTitle:categoryName forState:UIControlStateNormal];
[dropdown setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
} else {
[dropdown setTitle:@"(No Category)" forState:UIControlStateNormal];
[dropdown setTitleColor:[UIColor grayColor] forState:UIControlStateNormal];
}
dropdown.enabled = NO; // make it non-clickable
dropdown.alpha = 0.6; // visually indicate read-only
}
lastView = dropdown; lastView = dropdown;
} }
...@@ -357,22 +444,72 @@ ...@@ -357,22 +444,72 @@
return btn; return btn;
} }
- (void)handleDropdownTap:(UIButton *)sender { - (void)handleDropdownTap:(UIButton *)sender {
NSInteger tag = sender.tag;
NSString *title = sender.titleLabel.text ?: @"Select"; NSString *title = sender.titleLabel.text ?: @"Select";
UIAlertController *menu = [UIAlertController alertControllerWithTitle:title UIAlertController *menu = [UIAlertController alertControllerWithTitle:title
message:@"Pick an option" message:nil
preferredStyle:UIAlertControllerStyleActionSheet]; preferredStyle:UIAlertControllerStyleActionSheet];
NSArray *options = @[@"Option 1", @"Option 2", @"Option 3"]; NSArray *options = @[];
if (tag == 2001) { // Type dropdown
NSMutableArray *typeNames = [NSMutableArray array];
for (NSDictionary *type in self.types) {
[typeNames addObject:type[@"type_name"] ?: @"Unnamed"];
}
options = typeNames;
} else if (tag == 2002) { // Issue dropdown
// Need selected type first
if (!self.selectedTypeName) {
UIAlertController *warn = [UIAlertController alertControllerWithTitle:@"Select Type First"
message:@"Please select a type before choosing an issue."
preferredStyle:UIAlertControllerStyleAlert];
[warn addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleCancel handler:nil]];
[self presentViewController:warn animated:YES completion:nil];
return;
}
// Find issues for the selected type
NSArray *issuesForType = @[];
for (NSDictionary *type in self.types) {
if ([type[@"type_name"] isEqualToString:self.selectedTypeName]) {
issuesForType = type[@"issue"];
break;
}
}
NSMutableArray *issueNames = [NSMutableArray array];
for (NSDictionary *issue in issuesForType) {
[issueNames addObject:issue[@"issue_name"] ?: @"Unnamed"];
}
options = issueNames;
} else {
// Category (fixed)
if (self.categories.count > 0) {
NSDictionary *cat = self.categories.firstObject;
NSString *catName = cat[@"cat_name"] ?: @"-";
[sender setTitle:catName forState:UIControlStateNormal];
[sender setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
}
return;
}
// Build alert actions
for (NSString *opt in options) { for (NSString *opt in options) {
[menu addAction:[UIAlertAction actionWithTitle:opt [menu addAction:[UIAlertAction actionWithTitle:opt
style:UIAlertActionStyleDefault style:UIAlertActionStyleDefault
handler:^(UIAlertAction * _Nonnull action) { handler:^(UIAlertAction * _Nonnull action) {
[sender setTitle:opt forState:UIControlStateNormal]; [sender setTitle:opt forState:UIControlStateNormal];
[sender setTitleColor:[UIColor blackColor] forState:UIControlStateNormal]; [sender setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
if (tag == 2001) { // Type
self.selectedTypeName = opt;
// Reset issue button when type changes
[self.issueButton setTitle:@"- Select Issue -" forState:UIControlStateNormal];
[self.issueButton setTitleColor:[UIColor lightGrayColor] forState:UIControlStateNormal];
}
}]]; }]];
} }
...@@ -383,6 +520,7 @@ ...@@ -383,6 +520,7 @@
[self presentViewController:menu animated:YES completion:nil]; [self presentViewController:menu animated:YES completion:nil];
} }
- (UIButton *)makeUploadBox { - (UIButton *)makeUploadBox {
UIButton *upload = [UIButton buttonWithType:UIButtonTypeCustom]; // ✅ not System UIButton *upload = [UIButton buttonWithType:UIButtonTypeCustom]; // ✅ not System
upload.translatesAutoresizingMaskIntoConstraints = NO; upload.translatesAutoresizingMaskIntoConstraints = NO;
...@@ -525,20 +663,47 @@ didFinishPickingMediaWithInfo:(NSDictionary<UIImagePickerControllerInfoKey,id> * ...@@ -525,20 +663,47 @@ didFinishPickingMediaWithInfo:(NSDictionary<UIImagePickerControllerInfoKey,id> *
- (void)handleReset { - (void)handleReset {
NSLog(@"🔄 Reset button tapped"); NSLog(@"🔄 Reset button tapped");
self.locationField.text = @"";
// Leave Location and Category as-is
// Reset Type, Issue, Comment, and Uploaded Images
// Reset dropdowns for Type and Issue only
UIButton *typeBtn = [self.view viewWithTag:2001];
UIButton *issueBtn = [self.view viewWithTag:2002];
if ([typeBtn isKindOfClass:[UIButton class]]) {
[typeBtn setTitle:@"- Select Type -" forState:UIControlStateNormal];
[typeBtn setTitleColor:[UIColor lightGrayColor] forState:UIControlStateNormal];
}
if ([issueBtn isKindOfClass:[UIButton class]]) {
[issueBtn setTitle:@"- Select Issue -" forState:UIControlStateNormal];
[issueBtn setTitleColor:[UIColor lightGrayColor] forState:UIControlStateNormal];
}
// Reset comment box
self.commentField.text = @""; self.commentField.text = @"";
// Clear uploaded images
[self.uploadedImages removeAllObjects];
// (Optionally refresh your upload boxes visually if they show previews)
for (int i = 0; i < 3; i++) {
UIButton *uploadBtn = [self.view viewWithTag:(1000 + i)];
if ([uploadBtn isKindOfClass:[UIButton class]]) {
[uploadBtn setBackgroundImage:nil forState:UIControlStateNormal];
[uploadBtn setTitle:@"+" forState:UIControlStateNormal]; // or whatever placeholder you use
}
}
// Show confirmation alert
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Fields Cleared" UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Fields Cleared"
message:@"All inputs have been reset." message:@"All inputs except Location and Category have been reset."
preferredStyle:UIAlertControllerStyleAlert]; preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"OK" [alert addAction:[UIAlertAction actionWithTitle:@"OK"
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];
} }
#pragma mark - Actions #pragma mark - Actions
- (void)dismissSelf { - (void)dismissSelf {
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
#import "PlanViewController.h" #import "PlanViewController.h"
#import "DetailsViewController.h" #import "DetailsViewController.h"
#import "AddIssueAPIClient.h" #import "AddIssueAPIClient.h"
#import <AVFoundation/AVFoundation.h>
@interface PlanViewController () @interface PlanViewController ()
...@@ -17,6 +18,18 @@ ...@@ -17,6 +18,18 @@
@property (nonatomic, strong) NSString *planImageURL; @property (nonatomic, strong) NSString *planImageURL;
@property (nonatomic, assign) CGFloat planWidth; @property (nonatomic, assign) CGFloat planWidth;
@property (nonatomic, assign) CGFloat planHeight; @property (nonatomic, assign) CGFloat planHeight;
@property (nonatomic, strong) UIView *markerView;
@property (nonatomic, strong) UIView *footerView;
@property (nonatomic, strong) UILabel *selectedLabel;
@property (nonatomic, strong) UIButton *footerBackButton;
@property (nonatomic, strong) UIButton *footerNextButton;
@property (nonatomic, strong) CAShapeLayer *selectedOverlay;
@property (nonatomic, strong) NSMutableArray<CAShapeLayer *> *overlayLayers;
@property (nonatomic, assign) BOOL selectionLocked;
@property (nonatomic, strong) NSString *planID;
@property (nonatomic, strong) NSString *locationID;
@property (nonatomic, strong) NSDictionary *locationData;
@property (nonatomic, strong) NSString *defectMatrix;
@end @end
...@@ -31,6 +44,8 @@ ...@@ -31,6 +44,8 @@
[self createHeader]; [self createHeader];
[self createScrollView]; [self createScrollView];
self.overlayLayers = [NSMutableArray array];
[self fetchInitialData]; [self fetchInitialData];
[self.view bringSubviewToFront:self.headerView]; [self.view bringSubviewToFront:self.headerView];
} }
...@@ -56,6 +71,8 @@ ...@@ -56,6 +71,8 @@
} }
NSLog(@"✅ Raw Unit Plan response received"); NSLog(@"✅ Raw Unit Plan response received");
// Save response to Documents for inspection
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:data options:NSJSONWritingPrettyPrinted error:nil]; NSData *jsonData = [NSJSONSerialization dataWithJSONObject:data options:NSJSONWritingPrettyPrinted error:nil];
NSString *docsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject; NSString *docsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
NSString *filePath = [docsPath stringByAppendingPathComponent:@"getUnitPlan.txt"]; NSString *filePath = [docsPath stringByAppendingPathComponent:@"getUnitPlan.txt"];
...@@ -84,7 +101,7 @@ ...@@ -84,7 +101,7 @@
return; return;
} }
// ✅ Extract plan dimensions and image URL // ✅ Extract plan info from the first location’s first issue
NSDictionary *firstLocation = self.locations.firstObject; NSDictionary *firstLocation = self.locations.firstObject;
NSArray *issues = firstLocation[@"issue"]; NSArray *issues = firstLocation[@"issue"];
if (issues.count == 0) { if (issues.count == 0) {
...@@ -95,6 +112,9 @@ ...@@ -95,6 +112,9 @@
NSDictionary *firstIssue = issues.firstObject; NSDictionary *firstIssue = issues.firstObject;
NSString *widthStr = [NSString stringWithFormat:@"%@", firstIssue[@"plan_width"]]; NSString *widthStr = [NSString stringWithFormat:@"%@", firstIssue[@"plan_width"]];
NSString *heightStr = [NSString stringWithFormat:@"%@", firstIssue[@"plan_height"]]; NSString *heightStr = [NSString stringWithFormat:@"%@", firstIssue[@"plan_height"]];
self.planID = [NSString stringWithFormat:@"%@", firstIssue[@"plan_id"] ?: @"0"];
self.locationID = [NSString stringWithFormat:@"%@", firstIssue[@"location_id"] ?: @"0"];
self.planWidth = [widthStr floatValue]; self.planWidth = [widthStr floatValue];
self.planHeight = [heightStr floatValue]; self.planHeight = [heightStr floatValue];
self.planImageURL = firstIssue[@"plan_image"]; self.planImageURL = firstIssue[@"plan_image"];
...@@ -102,6 +122,7 @@ ...@@ -102,6 +122,7 @@
NSLog(@"📐 Plan dimensions: width = %f, height = %f", self.planWidth, self.planHeight); NSLog(@"📐 Plan dimensions: width = %f, height = %f", self.planWidth, self.planHeight);
NSLog(@"🗺 Found %lu locations", (unsigned long)self.locations.count); NSLog(@"🗺 Found %lu locations", (unsigned long)self.locations.count);
NSLog(@"🖼 Plan image URL: %@", self.planImageURL); NSLog(@"🖼 Plan image URL: %@", self.planImageURL);
NSLog(@"🧭 plan_id = %@, location_id = %@", self.planID, self.locationID);
// Update UI on main thread // Update UI on main thread
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
...@@ -139,6 +160,11 @@ ...@@ -139,6 +160,11 @@
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
self.planImageView.image = image; self.planImageView.image = image;
self.planImageView.userInteractionEnabled = YES;
UITapGestureRecognizer *tapRecognizer =
[[UITapGestureRecognizer alloc] initWithTarget:self
action:@selector(handleTapOnPlan:)];
[self.planImageView addGestureRecognizer:tapRecognizer];
[self.scrollView setZoomScale:1.0 animated:NO]; [self.scrollView setZoomScale:1.0 animated:NO];
[self.view setNeedsLayout]; [self.view setNeedsLayout];
[self.view layoutIfNeeded]; [self.view layoutIfNeeded];
...@@ -149,6 +175,75 @@ ...@@ -149,6 +175,75 @@
[task resume]; [task resume];
} }
- (void)handleTapOnPlan:(UITapGestureRecognizer *)recognizer {
if (self.selectionLocked) return;
CGPoint tapPoint = [recognizer locationInView:self.planImageView];
NSLog(@"👆 User tapped at: %@", NSStringFromCGPoint(tapPoint));
// Remove previous marker (if any)
[self.markerView removeFromSuperview];
// Add new marker
UIView *marker = [[UIView alloc] initWithFrame:CGRectMake(tapPoint.x - 6, tapPoint.y - 6, 12, 12)];
marker.backgroundColor = [UIColor redColor];
marker.layer.cornerRadius = 6;
marker.layer.borderColor = UIColor.whiteColor.CGColor;
marker.layer.borderWidth = 2.0;
[self.planImageView addSubview:marker];
self.markerView = marker;
// Check if tap inside any polygon
for (int i = 0; i < self.locations.count; i++) {
NSDictionary *locationData = self.locations[i];
CAShapeLayer *overlay = self.overlayLayers[i];
if ([self isPoint:tapPoint insideLocation:locationData]) {
NSLog(@"✅ Tap inside location: %@", locationData[@"name"]);
// Selected: Red border
overlay.strokeColor = [UIColor colorWithRed:1 green:0 blue:0 alpha:0.8].CGColor;
self.selectedOverlay = overlay;
self.selectionLocked = YES;
self.selectedLocation = locationData[@"name"];
[self showAddIssuePopup];
} else {
// Non-selected: Gray border, no fill
overlay.strokeColor = [UIColor colorWithWhite:0.6 alpha:0.5].CGColor;
overlay.fillColor = UIColor.clearColor.CGColor;
}
}
}
- (BOOL)isPoint:(CGPoint)point insideLocation:(NSDictionary *)locationData {
CGFloat planWidth = self.planWidth;
CGFloat planHeight = self.planHeight;
if (planWidth == 0 || planHeight == 0) return NO;
CGRect imageRect = AVMakeRectWithAspectRatioInsideRect(
CGSizeMake(planWidth, planHeight),
self.planImageView.bounds
);
CGFloat scaleX = imageRect.size.width / planWidth;
CGFloat scaleY = imageRect.size.height / planHeight;
NSArray *points = locationData[@"points"];
UIBezierPath *path = [UIBezierPath bezierPath];
for (int i = 0; i < points.count; i++) {
NSDictionary *p = points[i];
CGFloat x = imageRect.origin.x + [p[@"x"] doubleValue] * scaleX;
CGFloat y = imageRect.origin.y + [p[@"y"] doubleValue] * scaleY;
if (i == 0) [path moveToPoint:CGPointMake(x, y)];
else [path addLineToPoint:CGPointMake(x, y)];
}
[path closePath];
return [path containsPoint:point];
}
#pragma mark - UI Creation #pragma mark - UI Creation
- (void)createHeader { - (void)createHeader {
...@@ -436,8 +531,9 @@ ...@@ -436,8 +531,9 @@
polygonLayer.path = path.CGPath; polygonLayer.path = path.CGPath;
polygonLayer.fillColor = UIColor.clearColor.CGColor; polygonLayer.fillColor = UIColor.clearColor.CGColor;
polygonLayer.strokeColor = [UIColor colorWithRed:1 green:0 blue:0 alpha:0.3].CGColor; polygonLayer.strokeColor = [UIColor colorWithRed:1 green:0 blue:0 alpha:0.3].CGColor;
polygonLayer.lineWidth = 2.0; polygonLayer.lineWidth = 1.0;
[self.overlayLayers addObject:polygonLayer];
[planImageView.layer addSublayer:polygonLayer]; [planImageView.layer addSublayer:polygonLayer];
} }
...@@ -507,7 +603,8 @@ ...@@ -507,7 +603,8 @@
// Mock location name // Mock location name
UILabel *locationLabel = [[UILabel alloc] initWithFrame:CGRectMake(20, 50, popup.bounds.size.width - 40, 24)]; UILabel *locationLabel = [[UILabel alloc] initWithFrame:CGRectMake(20, 50, popup.bounds.size.width - 40, 24)];
locationLabel.text = @"Building A - 2nd Floor"; locationLabel.text = @"Building A - 2nd Floor";
self.selectedLocation = locationLabel.text; locationLabel.text = self.selectedLocation;
NSLog(@"self.selectedLocation %@", self.selectedLocation);
locationLabel.textColor = [UIColor blackColor]; locationLabel.textColor = [UIColor blackColor];
locationLabel.font = [UIFont boldSystemFontOfSize:17]; locationLabel.font = [UIFont boldSystemFontOfSize:17];
[popup addSubview:locationLabel]; [popup addSubview:locationLabel];
...@@ -550,6 +647,7 @@ ...@@ -550,6 +647,7 @@
- (void)dismissAddIssuePopup { - (void)dismissAddIssuePopup {
NSLog(@"❌ Dismissing Add Issue popup"); NSLog(@"❌ Dismissing Add Issue popup");
self.selectionLocked = NO;
UIView *overlay = [self.view viewWithTag:999]; UIView *overlay = [self.view viewWithTag:999];
UIView *popup = [self.view viewWithTag:1000]; UIView *popup = [self.view viewWithTag:1000];
...@@ -569,16 +667,84 @@ ...@@ -569,16 +667,84 @@
- (void)handleConfirmAddIssue { - (void)handleConfirmAddIssue {
NSLog(@"✅ Confirm Add Issue pressed"); NSLog(@"✅ Confirm Add Issue pressed");
// Dismiss popup first self.selectionLocked = NO;
[self dismissAddIssuePopup]; [self dismissAddIssuePopup];
// Then open AddIssueDetailsViewController after a short delay [AddIssueAPIClient fetchSettingsByLocation:^(NSDictionary *data, NSError *error) {
// Hide loader once we have a result
if (error) {
NSLog(@"❌ Error fetching settings: %@", error);
return;
}
NSDictionary *appData = data[@"AppData"];
NSDictionary *mainData = data[@"Data"];
NSNumber *statusCode = data[@"status_code"];
// === 1️⃣ Basic error checks ===
if (statusCode && statusCode.intValue != 200 && statusCode.intValue != 0) {
NSLog(@"Error, Server returned %@", statusCode);
return;
}
if ([appData[@"error_code"] integerValue] == 503) {
NSLog(@"Maintenance Mode, Server temporarily unavailable");
return;
}
// === 2️⃣ Success case ===
if ([appData[@"status"] isEqualToString:@"success"] && mainData) {
NSLog(@"✅ Location settings loaded successfully");
// Determine defectMatrix flag
NSString *defectMatrix = mainData ? @"YES" : @"NO";
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.25 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.25 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
AddIssueDetailsViewController *nextVC = [[AddIssueDetailsViewController alloc] init]; AddIssueDetailsViewController *nextVC = [[AddIssueDetailsViewController alloc] init];
nextVC.selectedLocationName = self.selectedLocation; // ✅ Pass it here
nextVC.modalPresentationStyle = UIModalPresentationFullScreen; nextVC.modalPresentationStyle = UIModalPresentationFullScreen;
NSLog(@"locationID %@ and planID %@", self.locationID, self.planID);
// ✅ Pass data forward
nextVC.planID = self.planID;
nextVC.locationID = self.locationID;
nextVC.selectedLocationName = self.selectedLocation;
nextVC.locationData = mainData;
nextVC.defectMatrix = defectMatrix;
nextVC.projectCode = self.projectCode;
nextVC.projectName = self.projectName;
NSLog(@"📦 Data being brought over:\n planID: %@\n locationID: %@\n selectedLocationName: %@\n defectMatrix: %@\n projectName: %@\n projectCode: %@\n locationData: %@",
nextVC.planID,
nextVC.locationID,
nextVC.selectedLocationName,
nextVC.defectMatrix,
nextVC.projectName,
nextVC.projectCode,
nextVC.locationData);
[self presentViewController:nextVC animated:YES completion:nil]; [self presentViewController:nextVC animated:YES completion:nil];
}); });
return;
}
// === 3️⃣ Fallback and special message handling ===
NSString *message = appData[@"message"] ?: @"Unknown server response.";
if ([message isEqualToString:@"user_not_found"]) {
NSLog(@"Session Expired, Please log in again");
// Optionally trigger logout flow here
} else if ([message.lowercaseString containsString:@"defect matrix not found"] ||
[message.lowercaseString containsString:@"location not found"]) {
// Proceed anyway but with defectMatrix=NO
AddIssueDetailsViewController *nextVC = [[AddIssueDetailsViewController alloc] init];
nextVC.planID = self.planID;
nextVC.locationID = self.locationID;
nextVC.selectedLocationName = self.selectedLocation;
nextVC.defectMatrix = @"NO";
[self presentViewController:nextVC animated:YES completion:nil];
} else {
NSLog(@"Error %@", message);
}
}];
} }
#pragma mark - Actions #pragma mark - Actions
......
...@@ -8,32 +8,32 @@ ...@@ -8,32 +8,32 @@
<key>BinaryPath</key> <key>BinaryPath</key>
<string>QmsPluginFramework.framework/QmsPluginFramework</string> <string>QmsPluginFramework.framework/QmsPluginFramework</string>
<key>LibraryIdentifier</key> <key>LibraryIdentifier</key>
<string>ios-arm64</string> <string>ios-arm64_x86_64-simulator</string>
<key>LibraryPath</key> <key>LibraryPath</key>
<string>QmsPluginFramework.framework</string> <string>QmsPluginFramework.framework</string>
<key>SupportedArchitectures</key> <key>SupportedArchitectures</key>
<array> <array>
<string>arm64</string> <string>arm64</string>
<string>x86_64</string>
</array> </array>
<key>SupportedPlatform</key> <key>SupportedPlatform</key>
<string>ios</string> <string>ios</string>
<key>SupportedPlatformVariant</key>
<string>simulator</string>
</dict> </dict>
<dict> <dict>
<key>BinaryPath</key> <key>BinaryPath</key>
<string>QmsPluginFramework.framework/QmsPluginFramework</string> <string>QmsPluginFramework.framework/QmsPluginFramework</string>
<key>LibraryIdentifier</key> <key>LibraryIdentifier</key>
<string>ios-arm64_x86_64-simulator</string> <string>ios-arm64</string>
<key>LibraryPath</key> <key>LibraryPath</key>
<string>QmsPluginFramework.framework</string> <string>QmsPluginFramework.framework</string>
<key>SupportedArchitectures</key> <key>SupportedArchitectures</key>
<array> <array>
<string>arm64</string> <string>arm64</string>
<string>x86_64</string>
</array> </array>
<key>SupportedPlatform</key> <key>SupportedPlatform</key>
<string>ios</string> <string>ios</string>
<key>SupportedPlatformVariant</key>
<string>simulator</string>
</dict> </dict>
</array> </array>
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment