Commit d7d2568a authored by Wei Han's avatar Wei Han

code update

parent b47a3b6b
{
"images" : [
{
"filename" : "icon_green_active.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"filename" : "icon_red_blocked.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"filename" : "Mask group.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"filename" : "Mask group (1).png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"filename" : "Mask group (2).png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
......@@ -6,7 +6,7 @@
<dict>
<key>Assets.car</key>
<data>
apewM0kg8bkb0nA0BqxaEqt0L8I=
1i2tuf6x04oClRQdI25h80iAWww=
</data>
<key>Headers/QmsPlugin.h</key>
<data>
......@@ -31,7 +31,7 @@
<dict>
<key>hash2</key>
<data>
kujF94lp6x0Gv/ur/rYZEuwSRGB/AosKCC73EYwiL8g=
1mZEAflZoNOaTXIhGRR7gPGBZ99fx2jFZ147Fkro4WM=
</data>
</dict>
<key>Headers/QmsPlugin.h</key>
......
......@@ -6,7 +6,7 @@
<dict>
<key>Assets.car</key>
<data>
apewM0kg8bkb0nA0BqxaEqt0L8I=
1i2tuf6x04oClRQdI25h80iAWww=
</data>
<key>Headers/QmsPlugin.h</key>
<data>
......@@ -31,7 +31,7 @@
<dict>
<key>hash2</key>
<data>
kujF94lp6x0Gv/ur/rYZEuwSRGB/AosKCC73EYwiL8g=
1mZEAflZoNOaTXIhGRR7gPGBZ99fx2jFZ147Fkro4WM=
</data>
</dict>
<key>Headers/QmsPlugin.h</key>
......
......@@ -16,6 +16,7 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, copy) NSString *projectId;
@property (nonatomic, copy) NSString *drawingPlanId;
@property (nonatomic, copy) NSString *projectLogo;
@property (nonatomic, copy) NSString *lotId;
@end
......
......@@ -15,6 +15,7 @@
#import "LocalStorage.h"
#import "APIConfig.h"
#import "ImageCacheHelper.h"
#import "ThirdPartyViewController.h"
@interface DashboardViewController () <UIScrollViewDelegate>
......@@ -366,8 +367,8 @@
footerView.clipsToBounds = YES;
[self.view addSubview:footerView];
NSArray *titles = @[ @"Make Appt.", @"Add Issue", @"Access Item" ];
NSArray *icons = @[ @"calendar.badge.plus", @"plus.circle", @"key.horizontal" ];
NSArray *titles = @[ @"Make Appt.", @"Add Issue", @"3rd Party Link", @"Access Item" ];
NSArray *icons = @[ @"calendar.badge.plus", @"plus.circle", @"link", @"key.horizontal" ];
CGFloat buttonWidth = self.view.bounds.size.width / titles.count;
CGFloat iconSize = 24.0;
......@@ -381,7 +382,7 @@
button.tintColor = [UIColor whiteColor];
UIImage *iconImage;
if (i == 2) {
if (i == 3) {
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
iconImage = [UIImage imageNamed:@"icon_white_acessitem"
inBundle:bundle
......@@ -446,6 +447,16 @@
break;
}
case 2: {
NSLog(@"Navigating to 3rd Party Link");
ThirdPartyViewController *linkVC = [[ThirdPartyViewController alloc] init];
linkVC.unitID = self.projectCode;
linkVC.unitName = self.projectName;
linkVC.lotId = self.lotId;
linkVC.modalPresentationStyle = UIModalPresentationFullScreen;
[self presentViewController:linkVC animated:YES completion:nil];
break;
}
case 3: {
NSLog(@"Navigating to Access Item");
AccessItemViewController *nextVC = [[AccessItemViewController alloc] init];
nextVC.unitID = self.projectCode;
......
......@@ -111,29 +111,29 @@
}
- (void)checkAndRefreshAPIs {
// NSLog(@"🔍 Checking credentials...");
// NSLog(@"ClientID: %@", self.ClientID);
// NSLog(@"ClientCode: %@", self.ClientCode);
// NSLog(@"user_token: %@", self.user_token);
//
// [self showLoadingOverlay:@"Loading..."];
//
// if (![self isReadyToLoadDashboard]) {
// NSLog(@"⏳ Waiting for all credentials to be ready...");
// return;
// }
//
// if (_isLoadingDashboard) return;
//
// self.isLoadingDashboard = YES;
//
// NSLog(@"✅ All credentials ready. ClientID: %@, ClientCode: %@, user_token: %@",
// self.ClientID, self.ClientCode, self.user_token);
//
//
// [APIConfig setAuthTokem:self.user_token];
// [APIConfig setClientId:self.ClientID];
self.ClientCode = @"spt";
NSLog(@"🔍 Checking credentials...");
NSLog(@"ClientID: %@", self.ClientID);
NSLog(@"ClientCode: %@", self.ClientCode);
NSLog(@"user_token: %@", self.user_token);
[self showLoadingOverlay:@"Loading..."];
if (![self isReadyToLoadDashboard]) {
NSLog(@"⏳ Waiting for all credentials to be ready...");
return;
}
if (_isLoadingDashboard) return;
self.isLoadingDashboard = YES;
NSLog(@"✅ All credentials ready. ClientID: %@, ClientCode: %@, user_token: %@",
self.ClientID, self.ClientCode, self.user_token);
[APIConfig setAuthTokem:self.user_token];
[APIConfig setClientId:self.ClientID];
// self.ClientCode = @"spt";
[self requestCompanyCode];
}
......
......@@ -284,6 +284,7 @@ didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
vc.drawingPlanId = drawingPlanId ?: @"";
vc.drawerController = self.drawerController;
vc.projectLogo = self.projectLogo;
vc.lotId = unit[@"lot_id"] ?: @"";
vc.modalPresentationStyle = UIModalPresentationFullScreen;
[self.navigationController pushViewController:vc animated:YES];
}
......
......@@ -5,6 +5,8 @@ NS_ASSUME_NONNULL_BEGIN
@interface ThirdPartyDetailsViewController : UIViewController
@property (nonatomic, strong) NSDictionary *item;
@end
NS_ASSUME_NONNULL_END
......
// ThirdDetailsViewController.mm
// ThirdDetailsViewController.mm
#import "ThirdDetailsViewController.h"
#import "APICLient.h"
@interface ThirdPartyDetailsViewController ()
@end
// Header
@property (nonatomic, strong) UIView *headerBar;
@property (nonatomic, strong) UILabel *titleLabel;
@property (nonatomic, strong) UIButton *backButton;
// Main Content
@property (nonatomic, strong) UIScrollView *scrollView;
@property (nonatomic, strong) UIView *contentView;
@property (nonatomic, strong) UIImageView *qrImageView;
@property (nonatomic, strong) UILabel *linkTitleLabel;
@property (nonatomic, strong) UILabel *createdOnLabel;
@property (nonatomic, strong) UILabel *linkStatusLabel;
@property (nonatomic, strong) UIButton *buttonCopy;
@property (nonatomic, strong) UIButton *shareButton;
@property (nonatomic, strong) UISwitch *accessSwitch;
@property (nonatomic, strong) UIButton *changeNameButton;
@property (nonatomic, strong) UIView *topContentView;
@property (nonatomic, strong) UIView *footerView;
@property (nonatomic, strong) UIButton *deleteFooterButton;
@property (nonatomic, strong) UIView *accessContainer;
@property (nonatomic, strong) UILabel *accessLabel;
@property (nonatomic, strong) UILabel *changeNameLabel;
@property (nonatomic, strong) UIImageView *arrowImageView;
// Loading overlay
@property (nonatomic, strong) UIView *loadingOverlay;
@property (nonatomic, strong) UIView *dimmedOverlay;
@property (nonatomic, strong) UIView *bottomSheet;
@property (nonatomic, strong) NSLayoutConstraint *bottomSheetBottomConstraint;
// For change name
@property (nonatomic, strong) UITextField *linkNameField;
@property (nonatomic, strong) UIButton *primaryButton;
@property (nonatomic, strong) UIView *addLinkSheet;
@property (nonatomic, strong) UIButton *createButton;
// Success modal
@property (nonatomic, strong) UIView *successOverlay;
@property (nonatomic, strong) UIView *successModal;
@property (nonatomic, strong) UIView *confirmOverlay;
@property (nonatomic, strong) UIView *confirmModal;
@end
@implementation ThirdPartyDetailsViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = UIColor.systemBackgroundColor;
NSLog(@"third party unit: %@", self.item);
[self setupHeader];
[self setupFooter];
[self setupScrollView]; // Setup scrollView FIRST
[self setupContent]; // Then setup content INSIDE scrollView
[self setupLoadingOverlay];
[self updateUI];
// Debug layout
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"TopContentView frame: %@", NSStringFromCGRect(self.topContentView.frame));
NSLog(@"AccessContainer frame: %@", NSStringFromCGRect(self.accessContainer.frame));
NSLog(@"ContentView frame: %@", NSStringFromCGRect(self.contentView.frame));
});
}
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
NSLog(@"After layout:");
NSLog(@"TopContentView frame: %@", NSStringFromCGRect(self.topContentView.frame));
NSLog(@"AccessContainer frame: %@", NSStringFromCGRect(self.accessContainer.frame));
NSLog(@"ContentView frame: %@", NSStringFromCGRect(self.contentView.frame));
NSLog(@"ScrollView contentSize: %@", NSStringFromCGSize(self.scrollView.contentSize));
}
#pragma mark - Header
- (void)setupHeader {
UILayoutGuide *safe = self.view.safeAreaLayoutGuide;
self.headerBar = [[UIView alloc] init];
self.headerBar.backgroundColor = [UIColor colorWithRed:0.0 green:0.43 blue:0.55 alpha:1.0]; // #006D8D
[self.view addSubview:self.headerBar];
self.headerBar.translatesAutoresizingMaskIntoConstraints = NO;
[NSLayoutConstraint activateConstraints:@[
[self.headerBar.topAnchor constraintEqualToAnchor:safe.topAnchor],
[self.headerBar.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
[self.headerBar.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
[self.headerBar.heightAnchor constraintEqualToConstant:44],
]];
self.titleLabel = [[UILabel alloc] init];
self.titleLabel.text = @"Manage 3rd Party Link";
self.titleLabel.textColor = UIColor.whiteColor;
self.titleLabel.font = [UIFont systemFontOfSize:18 weight:UIFontWeightMedium];
self.titleLabel.textAlignment = NSTextAlignmentCenter;
self.backButton = [UIButton buttonWithType:UIButtonTypeSystem];
[self.backButton setImage:[UIImage systemImageNamed:@"chevron.backward"] forState:UIControlStateNormal];
self.backButton.tintColor = UIColor.whiteColor;
[self.backButton addTarget:self
action:@selector(onBack)
forControlEvents:UIControlEventTouchUpInside];
for (UIView *sub in @[self.titleLabel, self.backButton]) {
sub.translatesAutoresizingMaskIntoConstraints = NO;
[self.headerBar addSubview:sub];
}
[NSLayoutConstraint activateConstraints:@[
[self.backButton.leadingAnchor constraintEqualToAnchor:self.headerBar.leadingAnchor constant:16],
[self.backButton.centerYAnchor constraintEqualToAnchor:self.headerBar.centerYAnchor],
[self.backButton.heightAnchor constraintEqualToConstant:24],
[self.backButton.widthAnchor constraintEqualToConstant:24],
[self.titleLabel.centerXAnchor constraintEqualToAnchor:self.headerBar.centerXAnchor],
[self.titleLabel.centerYAnchor constraintEqualToAnchor:self.headerBar.centerYAnchor],
]];
}
#pragma mark - ScrollView + Content View
- (void)setupScrollView {
self.scrollView = [[UIScrollView alloc] init];
self.scrollView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:self.scrollView];
self.contentView = [[UIView alloc] init];
self.contentView.translatesAutoresizingMaskIntoConstraints = NO;
[self.scrollView addSubview:self.contentView];
// ScrollView constraints
[NSLayoutConstraint activateConstraints:@[
[self.scrollView.topAnchor constraintEqualToAnchor:self.headerBar.bottomAnchor],
[self.scrollView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
[self.scrollView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
[self.scrollView.bottomAnchor constraintEqualToAnchor:self.footerView.topAnchor],
]];
// ContentView constraints - FIXED: Connect all 4 sides to scrollView
[NSLayoutConstraint activateConstraints:@[
[self.contentView.topAnchor constraintEqualToAnchor:self.scrollView.topAnchor],
[self.contentView.leadingAnchor constraintEqualToAnchor:self.scrollView.leadingAnchor],
[self.contentView.trailingAnchor constraintEqualToAnchor:self.scrollView.trailingAnchor],
[self.contentView.bottomAnchor constraintEqualToAnchor:self.scrollView.bottomAnchor],
[self.contentView.widthAnchor constraintEqualToAnchor:self.scrollView.widthAnchor],
// Height will be determined by content
]];
}
#pragma mark - Content (FINAL WORKING VERSION)
- (void)setupContent {
// Clear any existing views
for (UIView *view in self.contentView.subviews) {
[view removeFromSuperview];
}
// ========== CREATE ALL VIEWS ==========
// QR Code
self.qrImageView = [[UIImageView alloc] init];
self.qrImageView.backgroundColor = UIColor.systemGray5Color;
self.qrImageView.layer.cornerRadius = 12;
self.qrImageView.clipsToBounds = YES;
self.qrImageView.translatesAutoresizingMaskIntoConstraints = NO;
[self.contentView addSubview:self.qrImageView];
// Link title
self.linkTitleLabel = [[UILabel alloc] init];
self.linkTitleLabel.text = @"Link Title";
self.linkTitleLabel.font = [UIFont boldSystemFontOfSize:18];
self.linkTitleLabel.textAlignment = NSTextAlignmentCenter;
self.linkTitleLabel.translatesAutoresizingMaskIntoConstraints = NO;
[self.contentView addSubview:self.linkTitleLabel];
self.createdOnLabel = [[UILabel alloc] init];
self.createdOnLabel.text = @"Created On ";
self.createdOnLabel.font = [UIFont systemFontOfSize:14];
self.createdOnLabel.textAlignment = NSTextAlignmentCenter;
self.createdOnLabel.translatesAutoresizingMaskIntoConstraints = NO;
[self.contentView addSubview:self.createdOnLabel];
// Status pill
self.linkStatusLabel = [[UILabel alloc] init];
self.linkStatusLabel.text = @"Active";
self.linkStatusLabel.font = [UIFont systemFontOfSize:14 weight:UIFontWeightMedium];
self.linkStatusLabel.textColor = UIColor.whiteColor;
self.linkStatusLabel.textAlignment = NSTextAlignmentCenter;
self.linkStatusLabel.backgroundColor = [UIColor systemGreenColor];
self.linkStatusLabel.layer.cornerRadius = 12;
self.linkStatusLabel.clipsToBounds = YES;
self.linkStatusLabel.translatesAutoresizingMaskIntoConstraints = NO;
[self.contentView addSubview:self.linkStatusLabel];
// Buttons - CRITICAL: Create them with the helper method
self.buttonCopy = [self actionButtonWithImage:@"link" title:@"Copy Link"];
[self.buttonCopy addTarget:self action:@selector(handleCopyLink) forControlEvents:UIControlEventTouchUpInside];
self.buttonCopy.translatesAutoresizingMaskIntoConstraints = NO; // Ensure this is set
[self.contentView addSubview:self.buttonCopy];
UIView *divider = [[UIView alloc] init];
divider.backgroundColor = [UIColor colorWithWhite:0.85 alpha:1.0];
divider.translatesAutoresizingMaskIntoConstraints = NO;
[self.contentView addSubview:divider];
self.shareButton = [self actionButtonWithImage:@"arrowshape.turn.up.right" title:@"Share"];
[self.shareButton addTarget:self action:@selector(handleShareLink) forControlEvents:UIControlEventTouchUpInside];
self.shareButton.translatesAutoresizingMaskIntoConstraints = NO; // Ensure this is set
[self.contentView addSubview:self.shareButton];
// Access Container
self.accessContainer = [[UIView alloc] init];
self.accessContainer.backgroundColor = UIColor.whiteColor;
self.accessContainer.layer.cornerRadius = 24;
self.accessContainer.layer.borderWidth = 0.5;
self.accessContainer.layer.borderColor = [UIColor colorWithWhite:0.75 alpha:1.0].CGColor;
self.accessContainer.translatesAutoresizingMaskIntoConstraints = NO;
[self.contentView addSubview:self.accessContainer];
// Access Container internal views
UILabel *accessLabel = [[UILabel alloc] init];
accessLabel.text = @"Link Access";
accessLabel.font = [UIFont systemFontOfSize:16];
accessLabel.translatesAutoresizingMaskIntoConstraints = NO;
[self.accessContainer addSubview:accessLabel];
self.accessSwitch = [[UISwitch alloc] init];
self.accessSwitch.on = YES;
self.accessSwitch.translatesAutoresizingMaskIntoConstraints = NO;
[self.accessSwitch addTarget:self action:@selector(handleToggleAccess:) forControlEvents:UIControlEventValueChanged];
[self.accessContainer addSubview:self.accessSwitch];
UIView *innerDivider = [[UIView alloc] init];
innerDivider.backgroundColor = [UIColor colorWithWhite:0.9 alpha:1.0];
innerDivider.translatesAutoresizingMaskIntoConstraints = NO;
[self.accessContainer addSubview:innerDivider];
self.changeNameButton = [UIButton buttonWithType:UIButtonTypeSystem];
[self.changeNameButton setTitle:@"Change Link Name" forState:UIControlStateNormal];
self.changeNameButton.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
self.changeNameButton.titleLabel.font = [UIFont systemFontOfSize:16];
self.changeNameButton.translatesAutoresizingMaskIntoConstraints = NO;
[self.changeNameButton addTarget:self action:@selector(handleChangeName) forControlEvents:UIControlEventTouchUpInside];
[self.accessContainer addSubview:self.changeNameButton];
UIImageView *arrowImage = [[UIImageView alloc] initWithImage:[UIImage systemImageNamed:@"chevron.right"]];
arrowImage.tintColor = UIColor.systemGrayColor;
arrowImage.translatesAutoresizingMaskIntoConstraints = NO;
[self.accessContainer addSubview:arrowImage];
// ========== SETUP ALL CONSTRAINTS ==========
// 1. QR Image
[NSLayoutConstraint activateConstraints:@[
[self.qrImageView.topAnchor constraintEqualToAnchor:self.contentView.topAnchor constant:24],
[self.qrImageView.centerXAnchor constraintEqualToAnchor:self.contentView.centerXAnchor],
[self.qrImageView.widthAnchor constraintEqualToConstant:148],
[self.qrImageView.heightAnchor constraintEqualToConstant:148],
]];
// 2. Title Label
[NSLayoutConstraint activateConstraints:@[
[self.linkTitleLabel.topAnchor constraintEqualToAnchor:self.qrImageView.bottomAnchor constant:28],
[self.linkTitleLabel.leadingAnchor constraintEqualToAnchor:self.contentView.leadingAnchor constant:20],
[self.linkTitleLabel.trailingAnchor constraintEqualToAnchor:self.contentView.trailingAnchor constant:-20],
]];
// 3. Created On Label
[NSLayoutConstraint activateConstraints:@[
[self.createdOnLabel.topAnchor constraintEqualToAnchor:self.linkTitleLabel.bottomAnchor constant:8],
[self.createdOnLabel.leadingAnchor constraintEqualToAnchor:self.contentView.leadingAnchor constant:20],
[self.createdOnLabel.trailingAnchor constraintEqualToAnchor:self.contentView.trailingAnchor constant:-20],
]];
// 4. Status Label
[NSLayoutConstraint activateConstraints:@[
[self.linkStatusLabel.topAnchor constraintEqualToAnchor:self.createdOnLabel.bottomAnchor constant:28],
[self.linkStatusLabel.centerXAnchor constraintEqualToAnchor:self.contentView.centerXAnchor],
[self.linkStatusLabel.widthAnchor constraintEqualToConstant:80],
[self.linkStatusLabel.heightAnchor constraintEqualToConstant:24],
]];
// 5. Buttons Row - FIXED
[NSLayoutConstraint activateConstraints:@[
// Copy Button
[self.buttonCopy.topAnchor constraintEqualToAnchor:self.linkStatusLabel.bottomAnchor constant:32],
[self.buttonCopy.trailingAnchor constraintEqualToAnchor:self.contentView.centerXAnchor constant:-12],
[self.buttonCopy.widthAnchor constraintEqualToConstant:105],
[self.buttonCopy.heightAnchor constraintEqualToConstant:48],
// Divider
[divider.centerXAnchor constraintEqualToAnchor:self.contentView.centerXAnchor],
[divider.centerYAnchor constraintEqualToAnchor:self.buttonCopy.centerYAnchor],
[divider.widthAnchor constraintEqualToConstant:1],
[divider.heightAnchor constraintEqualToConstant:44],
// Share Button
[self.shareButton.topAnchor constraintEqualToAnchor:self.buttonCopy.topAnchor],
[self.shareButton.leadingAnchor constraintEqualToAnchor:self.contentView.centerXAnchor constant:12],
[self.shareButton.widthAnchor constraintEqualToConstant:105],
[self.shareButton.heightAnchor constraintEqualToConstant:48],
]];
// 6. Access Container
[NSLayoutConstraint activateConstraints:@[
[self.accessContainer.topAnchor constraintEqualToAnchor:self.buttonCopy.bottomAnchor constant:36],
[self.accessContainer.leadingAnchor constraintEqualToAnchor:self.contentView.leadingAnchor constant:20],
[self.accessContainer.trailingAnchor constraintEqualToAnchor:self.contentView.trailingAnchor constant:-20],
[self.accessContainer.heightAnchor constraintEqualToConstant:125],
]];
// 7. Access Container Internal
[NSLayoutConstraint activateConstraints:@[
[accessLabel.topAnchor constraintEqualToAnchor:self.accessContainer.topAnchor constant:16],
[accessLabel.leadingAnchor constraintEqualToAnchor:self.accessContainer.leadingAnchor constant:20],
[self.accessSwitch.centerYAnchor constraintEqualToAnchor:accessLabel.centerYAnchor],
[self.accessSwitch.trailingAnchor constraintEqualToAnchor:self.accessContainer.trailingAnchor constant:-20],
[innerDivider.leadingAnchor constraintEqualToAnchor:self.accessContainer.leadingAnchor constant:20],
[innerDivider.trailingAnchor constraintEqualToAnchor:self.accessContainer.trailingAnchor constant:-20],
[innerDivider.centerYAnchor constraintEqualToAnchor:self.accessContainer.centerYAnchor],
[innerDivider.heightAnchor constraintEqualToConstant:1],
[self.changeNameButton.topAnchor constraintEqualToAnchor:innerDivider.bottomAnchor constant:12],
[self.changeNameButton.leadingAnchor constraintEqualToAnchor:self.accessContainer.leadingAnchor constant:20],
[self.changeNameButton.trailingAnchor constraintEqualToAnchor:arrowImage.leadingAnchor constant:-8],
[self.changeNameButton.bottomAnchor constraintEqualToAnchor:self.accessContainer.bottomAnchor constant:-12],
[arrowImage.centerYAnchor constraintEqualToAnchor:self.changeNameButton.centerYAnchor],
[arrowImage.trailingAnchor constraintEqualToAnchor:self.accessContainer.trailingAnchor constant:-20],
[arrowImage.widthAnchor constraintEqualToConstant:12],
[arrowImage.heightAnchor constraintEqualToConstant:24],
]];
// 8. ContentView Bottom (defines scrollable height)
[NSLayoutConstraint activateConstraints:@[
[self.contentView.bottomAnchor constraintEqualToAnchor:self.accessContainer.bottomAnchor constant:100],
]];
}
- (UIButton *)actionButtonWithImage:(NSString *)imageName title:(NSString *)title {
UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];
button.tintColor = [UIColor colorWithRed:0.05 green:0.45 blue:0.57 alpha:1.0]; // #0E7392
button.translatesAutoresizingMaskIntoConstraints = NO; // CRITICAL: Add this!
UIImage *image = [UIImage systemImageNamed:imageName];
if (!image) {
image = [UIImage systemImageNamed:@"link"];
}
[button setImage:image forState:UIControlStateNormal];
[button setTitle:title forState:UIControlStateNormal];
button.titleLabel.font = [UIFont systemFontOfSize:14];
// Simple layout - image above text
button.imageView.contentMode = UIViewContentModeScaleAspectFit;
button.titleLabel.textAlignment = NSTextAlignmentCenter;
// Set image and title edge insets for vertical layout
CGFloat imageHeight = 24; // Approximate image height
CGFloat spacing = 4; // Space between image and text
button.imageEdgeInsets = UIEdgeInsetsMake(0, 0, title.length > 0 ? spacing + 10 : 0, 0);
button.titleEdgeInsets = UIEdgeInsetsMake(imageHeight + spacing, -image.size.width, 0, 0);
return button;
}
#pragma mark - Footer (Delete Button)
- (void)setupFooter {
UILayoutGuide *safe = self.view.safeAreaLayoutGuide;
self.footerView = [[UIView alloc] init];
self.footerView.translatesAutoresizingMaskIntoConstraints = NO;
self.footerView.backgroundColor = UIColor.clearColor;
[self.view addSubview:self.footerView];
self.deleteFooterButton = [UIButton buttonWithType:UIButtonTypeSystem];
[self.deleteFooterButton setTitle:@"Delete Link" forState:UIControlStateNormal];
[self.deleteFooterButton setTitleColor:[UIColor colorWithRed:1.0 green:0.39 blue:0.39 alpha:1.0] forState:UIControlStateNormal];
self.deleteFooterButton.layer.cornerRadius = 20;
self.deleteFooterButton.layer.borderWidth = 0.5;
self.deleteFooterButton.layer.borderColor = [UIColor colorWithRed:1.0 green:0.39 blue:0.39 alpha:1.0].CGColor;
self.deleteFooterButton.translatesAutoresizingMaskIntoConstraints = NO;
[self.deleteFooterButton addTarget:self action:@selector(presentDeleteConfirmModal) forControlEvents:UIControlEventTouchUpInside];
[self.footerView addSubview:self.deleteFooterButton];
[NSLayoutConstraint activateConstraints:@[
// Footer container
[self.footerView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
[self.footerView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
[self.footerView.bottomAnchor constraintEqualToAnchor:safe.bottomAnchor],
[self.footerView.heightAnchor constraintEqualToConstant:88],
// Button inside footer
[self.deleteFooterButton.topAnchor constraintEqualToAnchor:self.footerView.topAnchor constant:16],
[self.deleteFooterButton.heightAnchor constraintEqualToConstant:48],
[self.deleteFooterButton.centerXAnchor constraintEqualToAnchor:self.footerView.centerXAnchor],
[self.deleteFooterButton.widthAnchor constraintEqualToConstant:245],
]];
}
#pragma mark - Loading Overlay
- (void)setupLoadingOverlay {
self.loadingOverlay = [[UIView alloc] init];
self.loadingOverlay.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.3];
self.loadingOverlay.hidden = YES;
self.loadingOverlay.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:self.loadingOverlay];
[NSLayoutConstraint activateConstraints:@[
[self.loadingOverlay.topAnchor constraintEqualToAnchor:self.view.topAnchor],
[self.loadingOverlay.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
[self.loadingOverlay.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
[self.loadingOverlay.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor],
]];
}
#pragma mark - Actions
- (void)onBack {
[self dismissViewControllerAnimated:YES completion:nil];
}
-(void) updateUI{
if (!self.item) return;
self.linkTitleLabel.text = self.item[@"title"] ?: @"No Title";
NSString *createdOn = self.item[@"created_at"]; // e.g. "15-01-2026"
NSDateFormatter *inputFormatter = [[NSDateFormatter alloc] init];
[inputFormatter setDateFormat:@"dd-MM-yyyy"]; // matches the input string
NSDate *date = [inputFormatter dateFromString:createdOn];
if (date) {
NSDateFormatter *outputFormatter = [[NSDateFormatter alloc] init];
[outputFormatter setDateFormat:@"dd MMM yyyy"]; // desired output, e.g. 15 Jan 2026
NSString *formattedDate = [outputFormatter stringFromDate:date];
self.createdOnLabel.text = [NSString stringWithFormat:@"Created on %@", formattedDate];
} else {
self.createdOnLabel.text = [NSString stringWithFormat:@"Created on %@", createdOn]; // fallback
}
BOOL isActive = [self.item[@"status"] integerValue] == 1;
self.accessSwitch.on = isActive;
self.linkStatusLabel.text = isActive ? @"ACTIVE" : @"INACTIVE";
self.linkStatusLabel.backgroundColor = isActive ? UIColor.systemGreenColor : UIColor.systemGrayColor;
// QR code generation (simple placeholder)
NSString *urlCode = self.item[@"url_code"];
NSString *path = self.item[@"path"]; // "https://sptest.commudesk.com"
NSString *fullURL = [NSString stringWithFormat:@"%@/%@", path, urlCode];
// Generate QR code
self.qrImageView.image = [self generateQRCodeFromString:fullURL];
[self.contentView setNeedsLayout];
[self.contentView layoutIfNeeded];
}
- (UIImage *)generateQRCodeFromString:(NSString *)string {
NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
CIFilter *filter = [CIFilter filterWithName:@"CIQRCodeGenerator"];
[filter setValue:data forKey:@"inputMessage"];
[filter setValue:@"M" forKey:@"inputCorrectionLevel"];
CIImage *ciImage = filter.outputImage;
CGRect rect = CGRectIntegral(ciImage.extent);
CGFloat scale = 154 / CGRectGetWidth(rect);
size_t width = CGRectGetWidth(rect) * scale;
size_t height = CGRectGetHeight(rect) * scale;
CGColorSpaceRef cs = CGColorSpaceCreateDeviceGray();
CGContextRef context = CGBitmapContextCreate(nil, width, height, 8, 0, cs, kCGImageAlphaNone);
CIContext *ciContext = [CIContext context];
CGImageRef imageRef = [ciContext createCGImage:ciImage fromRect:rect];
CGContextSetInterpolationQuality(context, kCGInterpolationNone);
CGContextScaleCTM(context, scale, scale);
CGContextDrawImage(context, rect, imageRef);
CGImageRef scaledImage = CGBitmapContextCreateImage(context);
UIImage *image = [UIImage imageWithCGImage:scaledImage];
CGContextRelease(context);
CGColorSpaceRelease(cs);
CGImageRelease(imageRef);
CGImageRelease(scaledImage);
return image;
}
- (void)handleCopyLink {
NSString *path = self.item[@"path"] ?: @"";
NSString *urlCode = self.item[@"url_code"] ?: @"";
if (!path || !urlCode) return;
NSString *fullURL = [NSString stringWithFormat:@"%@/%@", path, urlCode];
UIPasteboard.generalPasteboard.string = fullURL;
[self showToast:@"Link Copied"];
}
- (void)handleShareLink {
self.dimmedOverlay = [self buildDimmedOverlay];
self.bottomSheet = [self buildShareSheet];
[self.view addSubview:self.dimmedOverlay];
[self.view addSubview:self.bottomSheet];
NSLayoutConstraint *bottom =
[self.bottomSheet.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor constant:300];
bottom.active = YES;
[NSLayoutConstraint activateConstraints:@[
[self.bottomSheet.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
[self.bottomSheet.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
[self.bottomSheet.heightAnchor constraintEqualToConstant:320],
]];
[self.view layoutIfNeeded];
[UIView animateWithDuration:0.3 animations:^{
self.dimmedOverlay.alpha = 1;
bottom.constant = 0;
[self.view layoutIfNeeded];
}];
}
- (void)handleChangeName {
NSLog(@"Show change name modal");
[self showAddLinkSheet];
}
- (void)showToast:(NSString *)message {
if (!message || message.length == 0) return;
CGFloat toastWidth = 250;
CGFloat toastHeight = 50;
UILabel *toastLabel = [[UILabel alloc] initWithFrame:CGRectZero];
toastLabel.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.7];
toastLabel.textColor = UIColor.whiteColor;
toastLabel.textAlignment = NSTextAlignmentCenter;
toastLabel.font = [UIFont systemFontOfSize:14 weight:UIFontWeightMedium];
toastLabel.text = message;
toastLabel.alpha = 0.0;
toastLabel.numberOfLines = 0;
toastLabel.layer.cornerRadius = 12;
toastLabel.clipsToBounds = YES;
toastLabel.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:toastLabel];
// Center it horizontally and place it near the bottom
[NSLayoutConstraint activateConstraints:@[
[toastLabel.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor],
[toastLabel.bottomAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.bottomAnchor constant:-80],
[toastLabel.widthAnchor constraintEqualToConstant:toastWidth],
[toastLabel.heightAnchor constraintEqualToConstant:toastHeight],
]];
// Animate toast in
[UIView animateWithDuration:0.3 animations:^{
toastLabel.alpha = 1.0;
} completion:^(BOOL finished) {
// After 1.5 seconds, fade it out
[UIView animateWithDuration:0.3 delay:1.5 options:0 animations:^{
toastLabel.alpha = 0.0;
} completion:^(BOOL finished) {
[toastLabel removeFromSuperview];
}];
}];
}
- (void)showAlert:(NSString *)title message:(NSString *)message {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]];
[self presentViewController:alert animated:YES completion:nil];
}
#pragma mark - Overlays
- (UIView *)buildDimmedOverlay {
UIView *overlay = [[UIView alloc] initWithFrame:self.view.bounds];
overlay.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.6];
overlay.alpha = 0;
UITapGestureRecognizer *tap =
[[UITapGestureRecognizer alloc] initWithTarget:self
action:@selector(dismissBottomSheet)];
[overlay addGestureRecognizer:tap];
return overlay;
}
- (UIView *)buildAddLinkSheet {
UIView *sheet = [[UIView alloc] init];
sheet.backgroundColor = UIColor.whiteColor;
sheet.layer.cornerRadius = 24;
sheet.layer.maskedCorners =
kCALayerMinXMinYCorner | kCALayerMaxXMinYCorner;
sheet.translatesAutoresizingMaskIntoConstraints = NO;
UILabel *title = [[UILabel alloc] init];
title.text = @"Change 3rd Party Link";
title.font = [UIFont systemFontOfSize:18 weight:UIFontWeightBold];
title.textAlignment = NSTextAlignmentCenter;
title.translatesAutoresizingMaskIntoConstraints = NO;
[sheet addSubview:title];
UILabel *subtitle = [[UILabel alloc] init];
subtitle.text = @"Enter name to allow temporary access via weblink to handle ongoing defects.";
subtitle.font = [UIFont systemFontOfSize:14];
subtitle.textAlignment = NSTextAlignmentCenter;
subtitle.textColor = UIColor.darkGrayColor;
subtitle.numberOfLines = 0;
subtitle.lineBreakMode = NSLineBreakByWordWrapping;
subtitle.translatesAutoresizingMaskIntoConstraints = NO;
[sheet addSubview:subtitle];
self.linkNameField = [[UITextField alloc] init];
self.linkNameField.placeholder = @"Enter Link Name";
self.linkNameField.borderStyle = UITextBorderStyleRoundedRect;
[self.linkNameField addTarget:self
action:@selector(onTextChanged:)
forControlEvents:UIControlEventEditingChanged];
self.linkNameField.translatesAutoresizingMaskIntoConstraints = NO;
[sheet addSubview:self.linkNameField];
UIButton *cancel = [UIButton buttonWithType:UIButtonTypeSystem];
[cancel setTitle:@"Cancel" forState:UIControlStateNormal];
[cancel addTarget:self
action:@selector(dismissBottomSheet)
forControlEvents:UIControlEventTouchUpInside];
[cancel setTitleColor:[UIColor colorWithRed:255.0/255.0
green:100.0/255.0
blue:100.0/255.0
alpha:1.0]
forState:UIControlStateNormal];
cancel.layer.cornerRadius = 10;
cancel.layer.borderWidth = 0.5;
cancel.layer.borderColor = [UIColor colorWithRed:255.0/255.0
green:100.0/255.0
blue:100.0/255.0
alpha:1.0].CGColor;
cancel.translatesAutoresizingMaskIntoConstraints = NO;
[sheet addSubview:cancel];
self.createButton = [UIButton buttonWithType:UIButtonTypeSystem];
[self.createButton setTitle:@"Update" forState:UIControlStateNormal];
self.createButton.enabled = NO;
self.createButton.backgroundColor = [UIColor colorWithRed:0.0 green:0.43 blue:0.55 alpha:1.0];
self.createButton.layer.cornerRadius = 10;
[self.createButton setTitleColor:UIColor.whiteColor forState:UIControlStateNormal];
[self.createButton addTarget:self
action:@selector(onCreateLink)
forControlEvents:UIControlEventTouchUpInside];
self.createButton.translatesAutoresizingMaskIntoConstraints = NO;
[sheet addSubview:self.createButton];
[NSLayoutConstraint activateConstraints:@[
[title.centerXAnchor constraintEqualToAnchor:sheet.centerXAnchor],
[title.topAnchor constraintEqualToAnchor:sheet.topAnchor constant:28],
[subtitle.topAnchor constraintEqualToAnchor:title.bottomAnchor constant:12],
[subtitle.leadingAnchor constraintEqualToAnchor:sheet.leadingAnchor constant:16],
[subtitle.trailingAnchor constraintEqualToAnchor:sheet.trailingAnchor constant:-16],
[self.linkNameField.topAnchor constraintEqualToAnchor:subtitle.bottomAnchor constant:24],
[self.linkNameField.leadingAnchor constraintEqualToAnchor:sheet.leadingAnchor constant:20],
[self.linkNameField.trailingAnchor constraintEqualToAnchor:sheet.trailingAnchor constant:-20],
[self.linkNameField.heightAnchor constraintEqualToConstant:48],
[cancel.topAnchor constraintEqualToAnchor:self.linkNameField.bottomAnchor constant:32],
[cancel.leadingAnchor constraintEqualToAnchor:subtitle.leadingAnchor],
[cancel.trailingAnchor constraintEqualToAnchor:sheet.centerXAnchor constant:-6],
[cancel.heightAnchor constraintEqualToConstant:48],
[self.createButton.topAnchor constraintEqualToAnchor:self.linkNameField.bottomAnchor constant:32],
[self.createButton.leadingAnchor constraintEqualToAnchor:sheet.centerXAnchor constant:6],
[self.createButton.trailingAnchor constraintEqualToAnchor:subtitle.trailingAnchor],
[self.createButton.heightAnchor constraintEqualToConstant:48],
[self.createButton.bottomAnchor constraintEqualToAnchor:sheet.safeAreaLayoutGuide.bottomAnchor constant:-24],
]];
return sheet;
}
- (void)onTextChanged:(UITextField *)textField {
BOOL hasText = textField.text.length > 0;
self.createButton.enabled = hasText;
self.createButton.backgroundColor =
hasText ? [UIColor colorWithRed:0.05 green:0.45 blue:0.57 alpha:1.0]
: UIColor.systemGray5Color;
}
- (void)showAddLinkSheet {
self.dimmedOverlay = [self buildDimmedOverlay];
self.addLinkSheet = [self buildAddLinkSheet];
[self.view addSubview:self.dimmedOverlay];
[self.view addSubview:self.addLinkSheet];
NSLayoutConstraint *bottom =
[self.addLinkSheet.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor constant:300];
bottom.active = YES;
[NSLayoutConstraint activateConstraints:@[
[self.addLinkSheet.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
[self.addLinkSheet.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
[self.addLinkSheet.heightAnchor constraintEqualToConstant:320],
]];
[self.view layoutIfNeeded];
[UIView animateWithDuration:0.3 animations:^{
self.dimmedOverlay.alpha = 1;
bottom.constant = 0;
[self.view layoutIfNeeded];
}];
}
- (UIView *)buildShareSheet {
UIView *sheet = [[UIView alloc] init];
sheet.backgroundColor = UIColor.whiteColor;
sheet.layer.cornerRadius = 24;
sheet.layer.maskedCorners =
kCALayerMinXMinYCorner | kCALayerMaxXMinYCorner;
sheet.translatesAutoresizingMaskIntoConstraints = NO;
UILabel *title = [[UILabel alloc] init];
title.text = @"Share 3rd Party Link URL";
title.font = [UIFont systemFontOfSize:18 weight:UIFontWeightBold];
title.textAlignment = NSTextAlignmentCenter;
title.translatesAutoresizingMaskIntoConstraints = NO;
[sheet addSubview:title];
UILabel *subtitle = [[UILabel alloc] init];
subtitle.text = @"Select an option to share Issue Inspector’s link.";
subtitle.font = [UIFont systemFontOfSize:14];
subtitle.textColor = UIColor.darkGrayColor;
subtitle.textAlignment = NSTextAlignmentCenter;
subtitle.translatesAutoresizingMaskIntoConstraints = NO;
[sheet addSubview:subtitle];
UIButton *copyBtn = [self shareActionButton:@"Copy Link"
image:@"doc.on.doc"
selector:@selector(handleCopyLink)];
UIButton *systemShareBtn = [self shareActionButton:@"Share via Apps"
image:@"square.and.arrow.up"
selector:@selector(handleSystemShare)];
UIButton *qrBtn = [self shareActionButton:@"Show QR Code"
image:@"qrcode"
selector:@selector(handleShowQR)];
for (UIButton *btn in @[copyBtn, systemShareBtn, qrBtn]) {
btn.translatesAutoresizingMaskIntoConstraints = NO;
[sheet addSubview:btn];
}
[NSLayoutConstraint activateConstraints:@[
[title.topAnchor constraintEqualToAnchor:sheet.topAnchor constant:28],
[title.centerXAnchor constraintEqualToAnchor:sheet.centerXAnchor],
[subtitle.topAnchor constraintEqualToAnchor:title.bottomAnchor constant:8],
[subtitle.leadingAnchor constraintEqualToAnchor:sheet.leadingAnchor constant:20],
[subtitle.trailingAnchor constraintEqualToAnchor:sheet.trailingAnchor constant:-20],
[copyBtn.topAnchor constraintEqualToAnchor:subtitle.bottomAnchor constant:24],
[copyBtn.leadingAnchor constraintEqualToAnchor:sheet.leadingAnchor constant:20],
[copyBtn.trailingAnchor constraintEqualToAnchor:sheet.trailingAnchor constant:-20],
[copyBtn.heightAnchor constraintEqualToConstant:48],
[systemShareBtn.topAnchor constraintEqualToAnchor:copyBtn.bottomAnchor constant:12],
[systemShareBtn.leadingAnchor constraintEqualToAnchor:copyBtn.leadingAnchor],
[systemShareBtn.trailingAnchor constraintEqualToAnchor:copyBtn.trailingAnchor],
[systemShareBtn.heightAnchor constraintEqualToConstant:48],
[qrBtn.topAnchor constraintEqualToAnchor:systemShareBtn.bottomAnchor constant:12],
[qrBtn.leadingAnchor constraintEqualToAnchor:copyBtn.leadingAnchor],
[qrBtn.trailingAnchor constraintEqualToAnchor:copyBtn.trailingAnchor],
[qrBtn.heightAnchor constraintEqualToConstant:48],
[qrBtn.bottomAnchor constraintEqualToAnchor:sheet.safeAreaLayoutGuide.bottomAnchor constant:-24],
]];
return sheet;
}
- (UIButton *)shareActionButton:(NSString *)title
image:(NSString *)systemImage
selector:(SEL)selector {
UIButton *btn = [UIButton buttonWithType:UIButtonTypeSystem];
btn.layer.cornerRadius = 10;
btn.layer.borderWidth = 0.5;
btn.layer.borderColor = [UIColor colorWithWhite:0.85 alpha:1.0].CGColor;
btn.tintColor = [UIColor colorWithRed:0.05 green:0.45 blue:0.57 alpha:1.0];
[btn setTitle:title forState:UIControlStateNormal];
[btn setImage:[UIImage systemImageNamed:systemImage] forState:UIControlStateNormal];
btn.titleLabel.font = [UIFont systemFontOfSize:16];
btn.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft;
btn.imageEdgeInsets = UIEdgeInsetsMake(0, 16, 0, 0);
btn.titleEdgeInsets = UIEdgeInsetsMake(0, 28, 0, 0);
[btn addTarget:self action:selector forControlEvents:UIControlEventTouchUpInside];
return btn;
}
- (void)dismissBottomSheet {
[UIView animateWithDuration:0.25 animations:^{
self.dimmedOverlay.alpha = 0;
self.bottomSheet.transform = CGAffineTransformMakeTranslation(0, 300);
self.addLinkSheet.transform = CGAffineTransformMakeTranslation(0, 300);
} completion:^(BOOL finished) {
[self.bottomSheet removeFromSuperview];
[self.addLinkSheet removeFromSuperview];
[self.dimmedOverlay removeFromSuperview];
self.bottomSheet = nil;
self.addLinkSheet = nil;
self.dimmedOverlay = nil;
}];
}
- (void)presentSuccessModal {
self.successOverlay = [[UIView alloc] initWithFrame:self.view.bounds];
self.successOverlay.backgroundColor =
[[UIColor blackColor] colorWithAlphaComponent:0.6];
self.successModal = [[UIView alloc] init];
self.successModal.backgroundColor = UIColor.whiteColor;
self.successModal.layer.cornerRadius = 24;
self.successModal.translatesAutoresizingMaskIntoConstraints = NO;
UILabel *title = [[UILabel alloc] init];
title.text = @"Success";
title.font = [UIFont systemFontOfSize:18 weight:UIFontWeightBold];
title.textAlignment = NSTextAlignmentCenter;
title.translatesAutoresizingMaskIntoConstraints = NO;
UILabel *subtitle = [[UILabel alloc] init];
subtitle.text = @"3rd party link has been successfully updated.";
subtitle.font = [UIFont systemFontOfSize:14];
subtitle.textAlignment = NSTextAlignmentCenter;
subtitle.textColor = UIColor.darkGrayColor;
subtitle.numberOfLines = 0;
subtitle.lineBreakMode = NSLineBreakByWordWrapping;
subtitle.translatesAutoresizingMaskIntoConstraints = NO;
UIButton *ok = [UIButton buttonWithType:UIButtonTypeSystem];
[ok setTitle:@"OK" forState:UIControlStateNormal];
[ok addTarget:self action:@selector(dismissSuccessModal) forControlEvents:UIControlEventTouchUpInside];
[ok setTitleColor:UIColor.whiteColor forState:UIControlStateNormal];
ok.layer.cornerRadius = 10;
ok.layer.backgroundColor = [UIColor colorWithRed:0.0 green:0.43 blue:0.55 alpha:1.0].CGColor;
ok.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:self.successOverlay];
[self.successOverlay addSubview:self.successModal];
[self.successModal addSubview:title];
[self.successModal addSubview:subtitle];
[self.successModal addSubview:ok];
[NSLayoutConstraint activateConstraints:@[
[self.successModal.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor],
[self.successModal.centerYAnchor constraintEqualToAnchor:self.view.centerYAnchor],
[self.successModal.widthAnchor constraintEqualToConstant:312],
[self.successModal.heightAnchor constraintEqualToConstant:309],
[title.topAnchor constraintEqualToAnchor:self.successModal.topAnchor constant:18],
[title.centerXAnchor constraintEqualToAnchor:self.successModal.centerXAnchor],
[subtitle.topAnchor constraintEqualToAnchor:title.bottomAnchor constant:12],
[subtitle.leadingAnchor constraintEqualToAnchor:self.successModal.leadingAnchor constant:20],
[subtitle.trailingAnchor constraintEqualToAnchor:self.successModal.trailingAnchor constant:-20],
[ok.topAnchor constraintEqualToAnchor:subtitle.bottomAnchor constant:32],
[ok.centerXAnchor constraintEqualToAnchor:self.successModal.centerXAnchor],
[ok.widthAnchor constraintEqualToConstant:166],
[ok.heightAnchor constraintEqualToConstant:48],
// [ok.bottomAnchor constraintEqualToAnchor:self.successModal.bottomAnchor constant:44],
]];
self.successOverlay.alpha = 0;
[UIView animateWithDuration:0.25 animations:^{
self.successOverlay.alpha = 1;
}];
}
- (void)dismissSuccessModal {
[UIView animateWithDuration:0.2 animations:^{
self.successOverlay.alpha = 0;
} completion:^(BOOL finished) {
[self.successOverlay removeFromSuperview];
[self onBack];
}];
}
- (void)presentDeleteConfirmModal {
self.confirmOverlay = [[UIView alloc] initWithFrame:self.view.bounds];
self.confirmOverlay.backgroundColor =
[[UIColor blackColor] colorWithAlphaComponent:0.6];
self.confirmModal = [[UIView alloc] init];
self.confirmModal.backgroundColor = UIColor.whiteColor;
self.confirmModal.layer.cornerRadius = 24;
self.confirmModal.translatesAutoresizingMaskIntoConstraints = NO;
UIImageView *icon = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"icon_alert_orange"]];
icon.translatesAutoresizingMaskIntoConstraints = NO;
UILabel *title = [[UILabel alloc] init];
title.text = @"Alert";
title.font = [UIFont systemFontOfSize:18 weight:UIFontWeightBold];
title.textAlignment = NSTextAlignmentCenter;
title.translatesAutoresizingMaskIntoConstraints = NO;
UILabel *message = [[UILabel alloc] init];
message.text = @"Confirm to delete this 3rd party link?";
message.font = [UIFont systemFontOfSize:14];
message.textColor = UIColor.darkGrayColor;
message.textAlignment = NSTextAlignmentCenter;
message.numberOfLines = 0;
message.translatesAutoresizingMaskIntoConstraints = NO;
UIButton *cancel = [UIButton buttonWithType:UIButtonTypeSystem];
[cancel setTitle:@"Cancel" forState:UIControlStateNormal];
cancel.layer.cornerRadius = 20;
cancel.layer.borderWidth = 0.5;
cancel.layer.borderColor = [UIColor colorWithRed:229/255.0
green:57/255.0
blue:53/255.0
alpha:1].CGColor;
[cancel setTitleColor:[UIColor colorWithRed:229/255.0
green:57/255.0
blue:53/255.0
alpha:1]
forState:UIControlStateNormal];
cancel.translatesAutoresizingMaskIntoConstraints = NO;
[cancel addTarget:self
action:@selector(dismissDeleteConfirmModal)
forControlEvents:UIControlEventTouchUpInside];
UIButton *confirm = [UIButton buttonWithType:UIButtonTypeSystem];
[confirm setTitle:@"Confirm" forState:UIControlStateNormal];
confirm.backgroundColor =
[UIColor colorWithRed:0.0 green:0.43 blue:0.55 alpha:1.0];
confirm.layer.cornerRadius = 20;
[confirm setTitleColor:UIColor.whiteColor forState:UIControlStateNormal];
confirm.translatesAutoresizingMaskIntoConstraints = NO;
[confirm addTarget:self
action:@selector(confirmDeleteLink)
forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:self.confirmOverlay];
[self.confirmOverlay addSubview:self.confirmModal];
[self.confirmModal addSubview:icon];
[self.confirmModal addSubview:title];
[self.confirmModal addSubview:message];
[self.confirmModal addSubview:cancel];
[self.confirmModal addSubview:confirm];
[NSLayoutConstraint activateConstraints:@[
[self.confirmModal.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor],
[self.confirmModal.centerYAnchor constraintEqualToAnchor:self.view.centerYAnchor],
[self.confirmModal.widthAnchor constraintEqualToConstant:309],
[self.confirmModal.heightAnchor constraintEqualToConstant:295],
[icon.topAnchor constraintEqualToAnchor:self.confirmModal.topAnchor constant:32],
[icon.centerXAnchor constraintEqualToAnchor:self.confirmModal.centerXAnchor],
[icon.widthAnchor constraintEqualToConstant:40],
[icon.heightAnchor constraintEqualToConstant:40],
[title.topAnchor constraintEqualToAnchor:icon.bottomAnchor constant:20],
[title.centerXAnchor constraintEqualToAnchor:self.confirmModal.centerXAnchor],
[message.topAnchor constraintEqualToAnchor:title.bottomAnchor constant:12],
[message.leadingAnchor constraintEqualToAnchor:self.confirmModal.leadingAnchor constant:20],
[message.trailingAnchor constraintEqualToAnchor:self.confirmModal.trailingAnchor constant:-20],
[cancel.topAnchor constraintEqualToAnchor:message.bottomAnchor constant:32],
[cancel.leadingAnchor constraintEqualToAnchor:self.confirmModal.leadingAnchor constant:20],
[cancel.heightAnchor constraintEqualToConstant:48],
[confirm.topAnchor constraintEqualToAnchor:message.bottomAnchor constant:32],
[confirm.trailingAnchor constraintEqualToAnchor:self.confirmModal.trailingAnchor constant:-20],
[confirm.leadingAnchor constraintEqualToAnchor:cancel.trailingAnchor constant:12],
[confirm.widthAnchor constraintEqualToAnchor:cancel.widthAnchor],
[confirm.heightAnchor constraintEqualToConstant:48],
]];
self.confirmOverlay.alpha = 0;
[UIView animateWithDuration:0.25 animations:^{
self.confirmOverlay.alpha = 1;
}];
}
- (void)dismissDeleteConfirmModal {
[UIView animateWithDuration:0.2 animations:^{
self.confirmOverlay.alpha = 0;
} completion:^(BOOL finished) {
[self.confirmOverlay removeFromSuperview];
self.confirmModal = nil;
self.confirmOverlay = nil;
}];
}
- (void)confirmDeleteLink {
[self dismissDeleteConfirmModal];
[self handleDeleteLink];
}
#pragma mark - API
- (void)sendUpdatePayload:(NSDictionary *)payload
success:(void(^)(void))success
error:(void(^)(void))error{
self.loadingOverlay.hidden = NO;
[APIClient updateThirdPartyLink:payload
completion:^(NSDictionary *response, NSError *error) {
self.loadingOverlay.hidden = YES;
if (error || !response) {
[self showAlert:@"Alert" message:@"Server error"];
return;
}
NSLog(@"Update response: %@", response);
if ([response[@"success"] boolValue]) {
if (success) success();
} else {
NSString *message = response[@"message"] ?: @"Something went wrong";
[self showAlert:@"Alert" message:message];
}
}];
}
- (void)handleToggleAccess:(UISwitch *)sender {
// Capture previous value
BOOL previousValue = !sender.isOn; // before API call, previous value is the opposite
NSInteger status = sender.isOn ? 1 : 0;
NSDictionary *payload = @{
@"id": self.item[@"id"],
@"status": @(status)
};
// Optimistically update the UI if you want, or leave as is
// [self updateUI]; // optional
[self sendUpdatePayload:payload success:^{
// Success: keep switch as is
[self updateUI];
} error:^{
// Failure: revert switch to previous value
sender.on = previousValue;
// Optional: show an alert
[self showAlert:@"Error" message:@"Failed to update link access. Please try again."];
}];
}
- (void)onCreateLink {
if (self.linkNameField.text.length == 0) return;
NSDictionary *payload = @{
@"id": self.item[@"id"],
@"title": self.linkNameField.text
};
[self sendUpdatePayload:payload success:^{
[self dismissBottomSheet];
[self presentSuccessModal];
} error:^{
[self showAlert:@"Error" message:@"Failed to update link name. Please try again."];
}
];
}
- (void)handleDeleteLink {
NSDictionary *payload = @{
@"id": self.item[@"id"],
@"status": @2
};
[self sendUpdatePayload:payload success:^{
[self onBack];
} error:^{
[self showAlert:@"Error" message:@"Failed to delete link. Please try again."];
}
];
}
- (void)handleSystemShare {
[self dismissBottomSheet];
NSString *fullURL =
[NSString stringWithFormat:@"%@/%@",
self.item[@"path"], self.item[@"url_code"]];
UIActivityViewController *vc =
[[UIActivityViewController alloc] initWithActivityItems:@[fullURL]
applicationActivities:nil];
[self presentViewController:vc animated:YES completion:nil];
}
- (void)handleShowQR {
[self dismissBottomSheet];
[self.scrollView scrollRectToVisible:self.qrImageView.frame animated:YES];
}
@end
......@@ -5,7 +5,11 @@
NS_ASSUME_NONNULL_BEGIN
@interface ThirdPartyViewController : UIViewController
@property (nonatomic, strong) NSString *unitID;
@property (nonatomic, strong) NSString *unitName;
@property (nonatomic, strong) NSString *lotId;
@end
NS_ASSUME_NONNULL_END
......
// ThirdPartyViewController.mm
#import "ThirdPartyViewController.h"
#import "ThirdTutorialViewController.h"
#import "ThirdDetailsViewController.h"
#import "APIClient.h"
@interface ThirdPartyViewController ()
@interface ThirdPartyCell : UITableViewCell
- (void)configureWithItem:(NSDictionary *)item;
@end
@interface ThirdPartyCell ()
// Container
@property (nonatomic, strong) UIView *cardView;
// Left
@property (nonatomic, strong) UIImageView *statusIcon;
// Center
@property (nonatomic, strong) UILabel *titleLabel;
@property (nonatomic, strong) UILabel *statusBadge;
// Right
@property (nonatomic, strong) UIImageView *arrowIcon;
@end
@implementation ThirdPartyCell
- (instancetype)initWithStyle:(UITableViewCellStyle)style
reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
self.backgroundColor = UIColor.clearColor;
self.selectionStyle = UITableViewCellSelectionStyleNone;
[self setupUI];
}
return self;
}
- (void)setupUI {
// Card container
self.cardView = [[UIView alloc] init];
self.cardView.backgroundColor = UIColor.whiteColor;
self.cardView.layer.cornerRadius = 24;
self.cardView.layer.masksToBounds = YES;
self.cardView.translatesAutoresizingMaskIntoConstraints = NO;
self.cardView.layer.borderWidth = 0.5;
self.cardView.layer.borderColor = UIColor.lightGrayColor.CGColor;
[self.contentView addSubview:self.cardView];
// Status icon
self.statusIcon = [[UIImageView alloc] init];
self.statusIcon.contentMode = UIViewContentModeScaleAspectFit;
self.statusIcon.translatesAutoresizingMaskIntoConstraints = NO;
// Title
self.titleLabel = [[UILabel alloc] init];
self.titleLabel.font = [UIFont systemFontOfSize:16 weight:UIFontWeightBold];
self.titleLabel.textColor = [UIColor colorWithWhite:0.2 alpha:1.0];
self.titleLabel.numberOfLines = 2;
self.titleLabel.translatesAutoresizingMaskIntoConstraints = NO;
// Status badge
self.statusBadge = [[UILabel alloc] init];
self.statusBadge.font = [UIFont systemFontOfSize:13];
self.statusBadge.textColor = UIColor.whiteColor;
self.statusBadge.textAlignment = NSTextAlignmentCenter;
self.statusBadge.layer.cornerRadius = 14;
self.statusBadge.layer.masksToBounds = YES;
self.statusBadge.translatesAutoresizingMaskIntoConstraints = NO;
// Arrow
self.arrowIcon = [[UIImageView alloc] initWithImage:
[UIImage systemImageNamed:@"chevron.right"]];
self.arrowIcon.tintColor = [UIColor colorWithRed:0.0 green:0.43 blue:0.55 alpha:1.0];
self.arrowIcon.translatesAutoresizingMaskIntoConstraints = NO;
for (UIView *v in @[self.statusIcon, self.titleLabel, self.statusBadge, self.arrowIcon]) {
[self.cardView addSubview:v];
}
// Constraints
[NSLayoutConstraint activateConstraints:@[
// Card
[self.cardView.topAnchor constraintEqualToAnchor:self.contentView.topAnchor constant:10],
[self.cardView.bottomAnchor constraintEqualToAnchor:self.contentView.bottomAnchor],
[self.cardView.leadingAnchor constraintEqualToAnchor:self.contentView.leadingAnchor constant:16],
[self.cardView.trailingAnchor constraintEqualToAnchor:self.contentView.trailingAnchor constant:-16],
[self.cardView.heightAnchor constraintEqualToConstant:98],
// Status icon
[self.statusIcon.leadingAnchor constraintEqualToAnchor:self.cardView.leadingAnchor constant:16],
[self.statusIcon.topAnchor constraintEqualToAnchor:self.cardView.topAnchor constant:16],
[self.statusIcon.widthAnchor constraintEqualToConstant:24],
[self.statusIcon.heightAnchor constraintEqualToConstant:24],
// Arrow
[self.arrowIcon.trailingAnchor constraintEqualToAnchor:self.cardView.trailingAnchor constant:-16],
[self.arrowIcon.centerYAnchor constraintEqualToAnchor:self.cardView.centerYAnchor],
[self.arrowIcon.widthAnchor constraintEqualToConstant:24],
[self.arrowIcon.heightAnchor constraintEqualToConstant:24],
// Title
[self.titleLabel.leadingAnchor constraintEqualToAnchor:self.statusIcon.trailingAnchor constant:16],
[self.titleLabel.trailingAnchor constraintEqualToAnchor:self.arrowIcon.leadingAnchor constant:-12],
[self.titleLabel.topAnchor constraintEqualToAnchor:self.cardView.topAnchor constant:16],
// Status badge
[self.statusBadge.leadingAnchor constraintEqualToAnchor:self.statusIcon.leadingAnchor],
[self.statusBadge.topAnchor constraintEqualToAnchor:self.titleLabel.bottomAnchor constant:12],
[self.statusBadge.widthAnchor constraintEqualToConstant:104],
[self.statusBadge.heightAnchor constraintEqualToConstant:28],
]];
}
- (void)configureWithItem:(NSDictionary *)item {
NSString *title = item[@"title"] ?: @"";
self.titleLabel.text = title;
BOOL isActive = [item[@"status"] integerValue] == 1;
// Status icon
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
self.statusIcon.image = [UIImage imageNamed:
(isActive ? @"icon_green_active" : @"icon_red_blocked")
inBundle:bundle
compatibleWithTraitCollection:nil];
// Status badge
self.statusBadge.text = isActive ? @"Active" : @"Blocked";
self.statusBadge.backgroundColor = isActive
? [UIColor colorWithRed:0.2 green:0.74 blue:0.29 alpha:1.0] // #32BC4A
: [UIColor colorWithRed:1.0 green:0.39 blue:0.39 alpha:1.0]; // #FF6464
}
@end
@interface ThirdPartyViewController () <UITableViewDelegate, UITableViewDataSource>
// Header (existing)
@property (nonatomic, strong) UIView *headerBar;
@property (nonatomic, strong) UILabel *titleLabel;
@property (nonatomic, strong) UIButton *backButton;
@property (nonatomic, strong) UIButton *helpButton;
@end
// Content
@property (nonatomic, strong) UIView *offlineBanner;
@property (nonatomic, strong) UITextField *searchField;
@property (nonatomic, strong) UITableView *tableView;
// Floating button
@property (nonatomic, strong) UIButton *addButton;
// Data
@property (nonatomic, strong) NSMutableArray *items;
@property (nonatomic, strong) NSMutableArray *filteredItems;
// State
@property (nonatomic, assign) BOOL isLoading;
@property (nonatomic, assign) BOOL isRefreshing;
@property (nonatomic, assign) BOOL showAddModal;
@property (nonatomic, assign) BOOL showSuccessModal;
// Add Link Bottom Sheet
@property (nonatomic, strong) UIView *dimmedOverlay;
@property (nonatomic, strong) UIView *addLinkSheet;
@property (nonatomic, strong) UITextField *linkNameField;
@property (nonatomic, strong) UIButton *createButton;
// Success Modal
@property (nonatomic, strong) UIView *successOverlay;
@property (nonatomic, strong) UIView *successModal;
@end
@implementation ThirdPartyViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = UIColor.whiteColor;
self.items = [NSMutableArray array];
self.filteredItems = [NSMutableArray array];
[self setupHeader];
// [self setupOfflineBanner];
[self setupSearchBar];
[self setupAddButton];
[self setupTableView];
[self requestThirdPartyList];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
// Refresh your list every time the view appears
[self requestThirdPartyList];
}
#pragma mark - Header
- (void)setupHeader {
UILayoutGuide *safe = self.view.safeAreaLayoutGuide;
//Header
self.headerBar = [[UIView alloc] init];
self.headerBar.backgroundColor = [UIColor colorWithRed:0.0 green:0.43 blue:0.55 alpha:1.0]; // #006D8D
self.headerBar.backgroundColor = [UIColor colorWithRed:0.0 green:0.43 blue:0.55 alpha:1.0];
[self.view addSubview:self.headerBar];
self.headerBar.translatesAutoresizingMaskIntoConstraints = NO;
......@@ -37,14 +220,14 @@
// Title
self.titleLabel = [[UILabel alloc] init];
self.titleLabel.text = @"Make Appointment";
self.titleLabel.text = @"3rd Party Link";
self.titleLabel.textColor = UIColor.whiteColor;
self.titleLabel.font = [UIFont systemFontOfSize:18 weight:UIFontWeightMedium];
self.titleLabel.textAlignment = NSTextAlignmentCenter;
// Back button
self.backButton = [UIButton buttonWithType:UIButtonTypeSystem];
[self.backButton setImage:[UIImage systemImageNamed:@"xmark"] forState:UIControlStateNormal];
[self.backButton setImage:[UIImage systemImageNamed:@"chevron.backward"] forState:UIControlStateNormal];
self.backButton.tintColor = UIColor.whiteColor;
[self.backButton addTarget:self
action:@selector(onBack)
......@@ -80,14 +263,497 @@
[self.helpButton.widthAnchor constraintEqualToConstant:24],
[self.helpButton.heightAnchor constraintEqualToConstant:24],
]];
}
#pragma mark - Offline mode
- (void)setupOfflineBanner {
self.offlineBanner = [[UIView alloc] init];
self.offlineBanner.backgroundColor = [UIColor colorWithRed:1.0 green:0.9 blue:0.9 alpha:1.0];
self.offlineBanner.hidden = YES; // toggle later
UILabel *label = [[UILabel alloc] init];
label.text = @"Offline mode";
label.font = [UIFont systemFontOfSize:13];
label.textColor = UIColor.darkGrayColor;
label.translatesAutoresizingMaskIntoConstraints = NO;
[self.offlineBanner addSubview:label];
[self.view addSubview:self.offlineBanner];
self.offlineBanner.translatesAutoresizingMaskIntoConstraints = NO;
[NSLayoutConstraint activateConstraints:@[
[self.offlineBanner.topAnchor constraintEqualToAnchor:self.headerBar.bottomAnchor],
[self.offlineBanner.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
[self.offlineBanner.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
[self.offlineBanner.heightAnchor constraintEqualToConstant:32],
[label.centerXAnchor constraintEqualToAnchor:self.offlineBanner.centerXAnchor],
[label.centerYAnchor constraintEqualToAnchor:self.offlineBanner.centerYAnchor],
]];
}
#pragma mark - Search Bar
- (void)setupSearchBar {
UIView *container = [[UIView alloc] init];
container.backgroundColor = [UIColor colorWithWhite:0.95 alpha:1.0];
container.layer.cornerRadius = 16;
self.searchField = [[UITextField alloc] init];
self.searchField.placeholder = @"Search Name or Unit Number";
self.searchField.font = [UIFont systemFontOfSize:14];
self.searchField.clearButtonMode = UITextFieldViewModeWhileEditing;
[self.searchField addTarget:self
action:@selector(searchChanged:)
forControlEvents:UIControlEventEditingChanged];
[container addSubview:self.searchField];
[self.view addSubview:container];
container.translatesAutoresizingMaskIntoConstraints = NO;
self.searchField.translatesAutoresizingMaskIntoConstraints = NO;
[NSLayoutConstraint activateConstraints:@[
[container.topAnchor constraintEqualToAnchor:self.headerBar.bottomAnchor constant:8],
[container.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:16],
[container.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:-16],
[container.heightAnchor constraintEqualToConstant:40],
[self.searchField.leadingAnchor constraintEqualToAnchor:container.leadingAnchor constant:16],
[self.searchField.trailingAnchor constraintEqualToAnchor:container.trailingAnchor constant:-16],
[self.searchField.centerYAnchor constraintEqualToAnchor:container.centerYAnchor],
]];
}
- (void)searchChanged:(UITextField *)textField {
[self filterWithText:textField.text];
}
#pragma mark - Table View
- (void)setupTableView {
self.tableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain];
self.tableView.tableFooterView = [UIView new];
self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;
self.tableView.rowHeight = UITableViewAutomaticDimension;
self.tableView.estimatedRowHeight = 98;
self.tableView.delegate = self;
self.tableView.dataSource = self;
self.tableView.backgroundColor = [UIColor colorWithWhite:0.85 alpha:1];
[self.view addSubview:self.tableView];
self.tableView.translatesAutoresizingMaskIntoConstraints = NO;
[self.tableView registerClass:ThirdPartyCell.class forCellReuseIdentifier:@"ThirdPartyCell"];
[NSLayoutConstraint activateConstraints:@[
[self.tableView.topAnchor constraintEqualToAnchor:self.searchField.superview.bottomAnchor constant:8],
[self.tableView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
[self.tableView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
[self.tableView.bottomAnchor constraintEqualToAnchor:self.addButton.topAnchor constant:-16],
]];
}
#pragma mark - UITableViewDataSource
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath {
ThirdPartyCell *cell =
[tableView dequeueReusableCellWithIdentifier:@"ThirdPartyCell"
forIndexPath:indexPath];
NSDictionary *item = self.filteredItems[indexPath.row];
[cell configureWithItem:item];
return cell;
}
- (NSInteger)tableView:(UITableView *)tableView
numberOfRowsInSection:(NSInteger)section {
return self.filteredItems.count;
}
- (CGFloat)tableView:(UITableView *)tableView
heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return UITableViewAutomaticDimension;
}
#pragma mark - UITableViewDelegate
- (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSDictionary *item = self.filteredItems[indexPath.row];
NSLog(@"Tapped item: %@", item[@"title"]);
ThirdPartyDetailsViewController *vc = [[ThirdPartyDetailsViewController alloc] init];
vc.item = item;
vc.modalPresentationStyle = UIModalPresentationFullScreen;
[self presentViewController:vc animated:YES completion:nil];
}
- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section {
return 50; // no extra spacing for other sections
}
#pragma mark - Footer Button
- (void)setupAddButton {
self.addButton = [UIButton buttonWithType:UIButtonTypeSystem];
self.addButton.backgroundColor = [UIColor colorWithRed:0.05 green:0.45 blue:0.57 alpha:1.0];
self.addButton.layer.cornerRadius = 20;
[self.addButton setTitle:@"Add" forState:UIControlStateNormal];
[self.addButton setTitleColor:UIColor.whiteColor forState:UIControlStateNormal];
self.addButton.titleLabel.font = [UIFont systemFontOfSize:14 weight:UIFontWeightMedium];
[self.addButton addTarget:self
action:@selector(onAddLink)
forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:self.addButton];
self.addButton.translatesAutoresizingMaskIntoConstraints = NO;
UILayoutGuide *safe = self.view.safeAreaLayoutGuide;
[NSLayoutConstraint activateConstraints:@[
[self.addButton.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor],
[self.addButton.bottomAnchor constraintEqualToAnchor:safe.bottomAnchor constant:-16],
[self.addButton.heightAnchor constraintEqualToConstant:48],
[self.addButton.widthAnchor constraintEqualToConstant:245],
]];
}
#pragma mark - Add Link
- (UIView *)buildDimmedOverlay {
UIView *overlay = [[UIView alloc] initWithFrame:self.view.bounds];
overlay.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.6];
overlay.alpha = 0;
UITapGestureRecognizer *tap =
[[UITapGestureRecognizer alloc] initWithTarget:self
action:@selector(dismissAddLinkSheet)];
[overlay addGestureRecognizer:tap];
return overlay;
}
- (UIView *)buildAddLinkSheet {
UIView *sheet = [[UIView alloc] init];
sheet.backgroundColor = UIColor.whiteColor;
sheet.layer.cornerRadius = 24;
sheet.layer.maskedCorners =
kCALayerMinXMinYCorner | kCALayerMaxXMinYCorner;
sheet.translatesAutoresizingMaskIntoConstraints = NO;
UILabel *title = [[UILabel alloc] init];
title.text = @"Add 3rd Party Link";
title.font = [UIFont systemFontOfSize:18 weight:UIFontWeightBold];
title.textAlignment = NSTextAlignmentCenter;
title.translatesAutoresizingMaskIntoConstraints = NO;
[sheet addSubview:title];
UILabel *subtitle = [[UILabel alloc] init];
subtitle.text = @"Enter name to allow temporary access via weblink to handle ongoing defects.";
subtitle.font = [UIFont systemFontOfSize:14];
subtitle.textAlignment = NSTextAlignmentCenter;
subtitle.textColor = UIColor.darkGrayColor;
subtitle.numberOfLines = 0;
subtitle.lineBreakMode = NSLineBreakByWordWrapping;
subtitle.translatesAutoresizingMaskIntoConstraints = NO;
[sheet addSubview:subtitle];
self.linkNameField = [[UITextField alloc] init];
self.linkNameField.placeholder = @"Enter Link Name";
self.linkNameField.borderStyle = UITextBorderStyleRoundedRect;
[self.linkNameField addTarget:self
action:@selector(onTextChanged:)
forControlEvents:UIControlEventEditingChanged];
self.linkNameField.translatesAutoresizingMaskIntoConstraints = NO;
[sheet addSubview:self.linkNameField];
UIButton *cancel = [UIButton buttonWithType:UIButtonTypeSystem];
[cancel setTitle:@"Cancel" forState:UIControlStateNormal];
[cancel addTarget:self
action:@selector(dismissAddLinkSheet)
forControlEvents:UIControlEventTouchUpInside];
[cancel setTitleColor:[UIColor colorWithRed:255.0/255.0
green:100.0/255.0
blue:100.0/255.0
alpha:1.0]
forState:UIControlStateNormal];
cancel.layer.cornerRadius = 10;
cancel.layer.borderWidth = 0.5;
cancel.layer.borderColor = [UIColor colorWithRed:255.0/255.0
green:100.0/255.0
blue:100.0/255.0
alpha:1.0].CGColor;
cancel.translatesAutoresizingMaskIntoConstraints = NO;
[sheet addSubview:cancel];
self.createButton = [UIButton buttonWithType:UIButtonTypeSystem];
[self.createButton setTitle:@"Create" forState:UIControlStateNormal];
self.createButton.enabled = NO;
self.createButton.backgroundColor = [UIColor colorWithRed:0.0 green:0.43 blue:0.55 alpha:1.0];
self.createButton.layer.cornerRadius = 10;
[self.createButton setTitleColor:UIColor.whiteColor forState:UIControlStateNormal];
[self.createButton addTarget:self
action:@selector(onCreateLink)
forControlEvents:UIControlEventTouchUpInside];
self.createButton.translatesAutoresizingMaskIntoConstraints = NO;
[sheet addSubview:self.createButton];
[NSLayoutConstraint activateConstraints:@[
[title.centerXAnchor constraintEqualToAnchor:sheet.centerXAnchor],
[title.topAnchor constraintEqualToAnchor:sheet.topAnchor constant:28],
[subtitle.topAnchor constraintEqualToAnchor:title.bottomAnchor constant:12],
[subtitle.leadingAnchor constraintEqualToAnchor:sheet.leadingAnchor constant:16],
[subtitle.trailingAnchor constraintEqualToAnchor:sheet.trailingAnchor constant:-16],
[self.linkNameField.topAnchor constraintEqualToAnchor:subtitle.bottomAnchor constant:24],
[self.linkNameField.leadingAnchor constraintEqualToAnchor:sheet.leadingAnchor constant:20],
[self.linkNameField.trailingAnchor constraintEqualToAnchor:sheet.trailingAnchor constant:-20],
[self.linkNameField.heightAnchor constraintEqualToConstant:48],
[cancel.topAnchor constraintEqualToAnchor:self.linkNameField.bottomAnchor constant:32],
[cancel.leadingAnchor constraintEqualToAnchor:subtitle.leadingAnchor],
[cancel.trailingAnchor constraintEqualToAnchor:sheet.centerXAnchor constant:-6],
[cancel.heightAnchor constraintEqualToConstant:48],
[self.createButton.topAnchor constraintEqualToAnchor:self.linkNameField.bottomAnchor constant:32],
[self.createButton.leadingAnchor constraintEqualToAnchor:sheet.centerXAnchor constant:6],
[self.createButton.trailingAnchor constraintEqualToAnchor:subtitle.trailingAnchor],
[self.createButton.heightAnchor constraintEqualToConstant:48],
[self.createButton.bottomAnchor constraintEqualToAnchor:sheet.safeAreaLayoutGuide.bottomAnchor constant:-24],
]];
return sheet;
}
- (void)showAddLinkSheet {
self.dimmedOverlay = [self buildDimmedOverlay];
self.addLinkSheet = [self buildAddLinkSheet];
[self.view addSubview:self.dimmedOverlay];
[self.view addSubview:self.addLinkSheet];
NSLayoutConstraint *bottom =
[self.addLinkSheet.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor constant:300];
bottom.active = YES;
[NSLayoutConstraint activateConstraints:@[
[self.addLinkSheet.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
[self.addLinkSheet.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
[self.addLinkSheet.heightAnchor constraintEqualToConstant:320],
]];
[self.view layoutIfNeeded];
[UIView animateWithDuration:0.3 animations:^{
self.dimmedOverlay.alpha = 1;
bottom.constant = 0;
[self.view layoutIfNeeded];
}];
}
- (void)dismissAddLinkSheet {
[UIView animateWithDuration:0.25 animations:^{
self.dimmedOverlay.alpha = 0;
self.addLinkSheet.transform = CGAffineTransformMakeTranslation(0, 300);
} completion:^(BOOL finished) {
[self.addLinkSheet removeFromSuperview];
[self.dimmedOverlay removeFromSuperview];
self.linkNameField.text = @"";
}];
}
- (void)onTextChanged:(UITextField *)textField {
BOOL hasText = textField.text.length > 0;
self.createButton.enabled = hasText;
self.createButton.backgroundColor =
hasText ? [UIColor colorWithRed:0.05 green:0.45 blue:0.57 alpha:1.0]
: UIColor.systemGray5Color;
}
- (void)onCreateLink {
// TODO: call API here
[APIClient createThirdPartyLink:self.linkNameField.text lotId:self.lotId completion:^(BOOL success, NSDictionary * _Nullable data, NSError * _Nullable error) {
if (!success) {
NSLog(@"❌ API returned success = NO");
return;
}
if (error || !data) {
NSLog(@"❌ Project list API failed: %@", error);
return;
}
// RN parity: status_code check
if (data[@"status_code"]) {
NSLog(@"❌ API status error: %@", data[@"message"]);
return;
}
NSDictionary *appData = data[@"AppData"];
if (![appData[@"status"] isEqualToString:@"success"]) {
NSLog(@"❌ API failure: %@", appData[@"message"]);
return;
}
if (!error) {
NSLog(@"Successfully created ThirdPartyLink");
[self dismissAddLinkSheet];
[self presentSuccessModal];
[self requestThirdPartyList];
}
}];
}
- (void)presentSuccessModal {
self.successOverlay = [[UIView alloc] initWithFrame:self.view.bounds];
self.successOverlay.backgroundColor =
[[UIColor blackColor] colorWithAlphaComponent:0.6];
self.successModal = [[UIView alloc] init];
self.successModal.backgroundColor = UIColor.whiteColor;
self.successModal.layer.cornerRadius = 24;
self.successModal.translatesAutoresizingMaskIntoConstraints = NO;
UILabel *title = [[UILabel alloc] init];
title.text = @"Success";
title.font = [UIFont systemFontOfSize:18 weight:UIFontWeightBold];
title.textAlignment = NSTextAlignmentCenter;
title.translatesAutoresizingMaskIntoConstraints = NO;
UILabel *subtitle = [[UILabel alloc] init];
subtitle.text = @"3rd party link has been successfully created.";
subtitle.font = [UIFont systemFontOfSize:14];
subtitle.textAlignment = NSTextAlignmentCenter;
subtitle.textColor = UIColor.darkGrayColor;
subtitle.numberOfLines = 0;
subtitle.lineBreakMode = NSLineBreakByWordWrapping;
subtitle.translatesAutoresizingMaskIntoConstraints = NO;
UIButton *ok = [UIButton buttonWithType:UIButtonTypeSystem];
[ok setTitle:@"OK" forState:UIControlStateNormal];
[ok addTarget:self action:@selector(dismissSuccessModal) forControlEvents:UIControlEventTouchUpInside];
[ok setTitleColor:UIColor.whiteColor forState:UIControlStateNormal];
ok.layer.cornerRadius = 10;
ok.layer.backgroundColor = [UIColor colorWithRed:0.0 green:0.43 blue:0.55 alpha:1.0].CGColor;
ok.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:self.successOverlay];
[self.successOverlay addSubview:self.successModal];
[self.successOverlay addSubview:title];
[self.successOverlay addSubview:subtitle];
[self.successOverlay addSubview:ok];
[NSLayoutConstraint activateConstraints:@[
[self.successModal.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor],
[self.successModal.centerYAnchor constraintEqualToAnchor:self.view.centerYAnchor],
[self.successModal.widthAnchor constraintEqualToConstant:312],
[self.successModal.heightAnchor constraintEqualToConstant:309],
[title.topAnchor constraintEqualToAnchor:self.successModal.topAnchor constant:18],
[title.centerXAnchor constraintEqualToAnchor:self.successModal.centerXAnchor],
[subtitle.topAnchor constraintEqualToAnchor:title.bottomAnchor constant:12],
[subtitle.leadingAnchor constraintEqualToAnchor:self.successModal.leadingAnchor constant:20],
[subtitle.trailingAnchor constraintEqualToAnchor:self.successModal.trailingAnchor constant:-20],
[ok.topAnchor constraintEqualToAnchor:subtitle.bottomAnchor constant:32],
[ok.centerXAnchor constraintEqualToAnchor:self.successModal.centerXAnchor],
[ok.widthAnchor constraintEqualToConstant:166],
[ok.heightAnchor constraintEqualToConstant:48],
[ok.bottomAnchor constraintEqualToAnchor:self.successModal.bottomAnchor constant:44],
]];
self.successOverlay.alpha = 0;
[UIView animateWithDuration:0.25 animations:^{
self.successOverlay.alpha = 1;
}];
}
- (void)dismissSuccessModal {
[UIView animateWithDuration:0.2 animations:^{
self.successOverlay.alpha = 0;
} completion:^(BOOL finished) {
[self.successOverlay removeFromSuperview];
}];
}
#pragma mark - API
-(void)requestThirdPartyList {
[APIClient requestThirdPartyList:^(BOOL success, NSDictionary * _Nullable data, NSError * _Nullable error) {
if (!success || ![data[@"Data"] isKindOfClass:[NSArray class]]) {
return;
}
NSLog(@"📦 Received thirdPartyList: %@", data);
// 1️⃣ Write to file for debugging (your existing logic)
NSString *plainText = [NSString stringWithFormat:@"%@", data];
NSString *docsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
NSString *filePath = [docsPath stringByAppendingPathComponent:@"thirdPartyList.txt"];
[plainText writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:nil];
NSLog(@"📁 thirdPartyList saved as plain text to: %@", filePath);
NSArray *list = data[@"Data"];
dispatch_async(dispatch_get_main_queue(), ^{
[self.items removeAllObjects];
[self.filteredItems removeAllObjects];
[self.items addObjectsFromArray:list];
[self.filteredItems addObjectsFromArray:list];
NSLog(@"Items count: %lu", (unsigned long)self.items.count);
NSLog(@"Filtered count: %lu", (unsigned long)self.filteredItems.count);
[self.tableView reloadData];
});
}];
}
#pragma mark - helpers
- (void)onBack {
[self.navigationController popViewControllerAnimated:YES];
[self dismissViewControllerAnimated:YES completion:nil];
}
@end
- (void)showTutorial {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Help"
message:@"This would show the tutorial (e.g. step1/step2/step3)."
preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]];
ThirdPartyTutorialViewController *vc = [[ThirdPartyTutorialViewController alloc] init];
vc.modalPresentationStyle = UIModalPresentationFullScreen;
[self presentViewController:vc animated:YES completion:nil];
}
- (void)onAddLink {
[self showAddLinkSheet];
}
- (void)filterWithText:(NSString *)text {
[self.filteredItems removeAllObjects];
if (text.length == 0) {
[self.filteredItems addObjectsFromArray:self.items];
} else {
for (NSDictionary *item in self.items) {
NSString *title = item[@"title"] ?: @"";
if ([[title lowercaseString] containsString:[text lowercaseString]]) {
[self.filteredItems addObject:item];
}
}
}
[self.tableView reloadData];
}
@end
// ThirdTutorialViewController.h
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface ThirdPartyTutorialViewController : UIViewController
@end
NS_ASSUME_NONNULL_END
// ThirdTutorialViewController.mm
#import "ThirdTutorialViewController.h"
@interface ThirdPartyTutorialViewController () <UIScrollViewDelegate>
@property (nonatomic, strong) UIView *headerBar;
@property (nonatomic, strong) UILabel *titleLabel;
@property (nonatomic, strong) UIButton *backButton;
@property (nonatomic, strong) UIButton *helpButton;
@property (nonatomic, strong) UIButton *skipButton;
@property (nonatomic, strong) UIScrollView *scrollView;
@property (nonatomic, strong) UIPageControl *pageControl;
@property (nonatomic, strong) UIButton *nextButton;
@property (nonatomic, strong) NSArray<NSDictionary *> *slides;
@property (nonatomic, assign) NSInteger currentIndex;
@end
@implementation ThirdPartyTutorialViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = UIColor.whiteColor;
[self setupHeader];
self.slides = @[
@{
@"title": @"Need someone to handle ongoing defects on your behalf?",
@"subtitle": @"3rd Party Link is your go-to feature for assigning and allowing authorised person to help submit your defects.",
@"image": @"img_tutorial_tpl01" // just the string
},
@{
@"title": @"Create temporary access via weblink for your representatives",
@"subtitle": @"The link created and shared by you will allow your authorised person to view the submitted issues.",
@"image": @"img_tutorial_tpl02"
},
@{
@"title": @"Change link access at your convenience",
@"subtitle": @"Toggle to switch link access when you no longer need the authorised person’s assistance.",
@"image": @"img_tutorial_tpl03"
}
];
self.currentIndex = 0;
[self setupScrollView];
[self setupPageControl];
[self setupNextButton];
}
#pragma mark - Header
- (void)setupHeader {
UILayoutGuide *safe = self.view.safeAreaLayoutGuide;
//Header
self.headerBar = [[UIView alloc] init];
self.headerBar.backgroundColor = [UIColor colorWithRed:0.0 green:0.43 blue:0.55 alpha:1.0]; // #006D8D
[self.view addSubview:self.headerBar];
self.headerBar.translatesAutoresizingMaskIntoConstraints = NO;
[NSLayoutConstraint activateConstraints:@[
[self.headerBar.topAnchor constraintEqualToAnchor:safe.topAnchor],
[self.headerBar.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
[self.headerBar.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
[self.headerBar.heightAnchor constraintEqualToConstant:44],
]];
// Title
self.titleLabel = [[UILabel alloc] init];
self.titleLabel.text = @"What is 3rd Party Link?";
self.titleLabel.textColor = UIColor.whiteColor;
self.titleLabel.font = [UIFont systemFontOfSize:18 weight:UIFontWeightMedium];
self.titleLabel.textAlignment = NSTextAlignmentCenter;
// Back button
self.backButton = [UIButton buttonWithType:UIButtonTypeSystem];
[self.backButton setImage:[UIImage systemImageNamed:@"chevron.backward"] forState:UIControlStateNormal];
self.backButton.tintColor = UIColor.whiteColor;
[self.backButton addTarget:self
action:@selector(onBack)
forControlEvents:UIControlEventTouchUpInside];
for (UIView *sub in @[self.titleLabel, self.backButton]) {
sub.translatesAutoresizingMaskIntoConstraints = NO;
[self.headerBar addSubview:sub];
}
// Skip button
self.skipButton = [UIButton buttonWithType:UIButtonTypeSystem];
[self.skipButton setTitle:@"Skip" forState:UIControlStateNormal];
[self.skipButton setTitleColor:UIColor.whiteColor forState:UIControlStateNormal];
self.skipButton.titleLabel.font = [UIFont systemFontOfSize:14 weight:UIFontWeightRegular];
self.skipButton.layer.cornerRadius = 20;
self.skipButton.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.5]; // semi-transparent
[self.skipButton addTarget:self action:@selector(onSkip) forControlEvents:UIControlEventTouchUpInside];
self.skipButton.translatesAutoresizingMaskIntoConstraints = NO;
[self.headerBar addSubview:self.skipButton];
[NSLayoutConstraint activateConstraints:@[
[self.backButton.leadingAnchor constraintEqualToAnchor:self.headerBar.leadingAnchor constant:16],
[self.backButton.centerYAnchor constraintEqualToAnchor:self.headerBar.centerYAnchor],
[self.backButton.heightAnchor constraintEqualToConstant:24],
[self.backButton.widthAnchor constraintEqualToConstant:24],
[self.titleLabel.centerXAnchor constraintEqualToAnchor:self.headerBar.centerXAnchor],
[self.titleLabel.centerYAnchor constraintEqualToAnchor:self.headerBar.centerYAnchor],
[self.skipButton.trailingAnchor constraintEqualToAnchor:self.headerBar.trailingAnchor constant:-12],
[self.skipButton.centerYAnchor constraintEqualToAnchor:self.headerBar.centerYAnchor],
[self.skipButton.heightAnchor constraintEqualToConstant:28],
[self.skipButton.widthAnchor constraintEqualToConstant:64],
]];
}
#pragma mark - Scroll View
- (void)setupScrollView {
self.scrollView = [[UIScrollView alloc] init];
self.scrollView.pagingEnabled = YES;
self.scrollView.showsHorizontalScrollIndicator = NO;
self.scrollView.delegate = self;
self.scrollView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:self.scrollView];
UILayoutGuide *safe = self.view.safeAreaLayoutGuide;
[NSLayoutConstraint activateConstraints:@[
[self.scrollView.topAnchor constraintEqualToAnchor:self.headerBar.bottomAnchor],
[self.scrollView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
[self.scrollView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
[self.scrollView.bottomAnchor constraintEqualToAnchor:safe.bottomAnchor constant:-100] // leave space for pageControl + button
]];
UIView *contentView = [[UIView alloc] init];
contentView.translatesAutoresizingMaskIntoConstraints = NO;
[self.scrollView addSubview:contentView];
[NSLayoutConstraint activateConstraints:@[
[contentView.topAnchor constraintEqualToAnchor:self.scrollView.topAnchor],
[contentView.bottomAnchor constraintEqualToAnchor:self.scrollView.bottomAnchor],
[contentView.leadingAnchor constraintEqualToAnchor:self.scrollView.leadingAnchor],
[contentView.trailingAnchor constraintEqualToAnchor:self.scrollView.trailingAnchor],
[contentView.heightAnchor constraintEqualToAnchor:self.scrollView.heightAnchor]
]];
UIView *previousSlide = nil;
for (NSDictionary *slide in self.slides) {
UIView *slideView = [self createSlideWithData:slide];
[contentView addSubview:slideView];
slideView.translatesAutoresizingMaskIntoConstraints = NO;
[NSLayoutConstraint activateConstraints:@[
[slideView.topAnchor constraintEqualToAnchor:contentView.topAnchor],
[slideView.bottomAnchor constraintEqualToAnchor:contentView.bottomAnchor],
[slideView.widthAnchor constraintEqualToAnchor:self.view.widthAnchor]
]];
if (previousSlide) {
[slideView.leadingAnchor constraintEqualToAnchor:previousSlide.trailingAnchor].active = YES;
} else {
[slideView.leadingAnchor constraintEqualToAnchor:contentView.leadingAnchor].active = YES;
}
previousSlide = slideView;
}
[previousSlide.trailingAnchor constraintEqualToAnchor:contentView.trailingAnchor].active = YES;
}
#pragma mark - UIScrollViewDelegate
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
NSInteger pageIndex = round(scrollView.contentOffset.x / scrollView.frame.size.width);
self.currentIndex = pageIndex;
self.pageControl.currentPage = pageIndex;
// change next button title on last page
if (pageIndex == self.slides.count - 1) {
[self.nextButton setTitle:@"OK" forState:UIControlStateNormal];
self.skipButton.hidden = YES; // hide skip
} else {
[self.nextButton setTitle:@"Next" forState:UIControlStateNormal];
self.skipButton.hidden = NO; // show skip
}
}
- (void)onNextPress {
if (self.currentIndex < self.slides.count - 1) {
self.currentIndex++;
[self.scrollView setContentOffset:CGPointMake(self.scrollView.frame.size.width * self.currentIndex, 0) animated:YES];
self.pageControl.currentPage = self.currentIndex;
// hide skip on last slide
if (self.currentIndex == self.slides.count - 1) {
self.skipButton.hidden = YES;
}
} else {
[self finishTutorial];
}
}
- (void)finishTutorial {
// store flag in NSUserDefaults
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"HAS_SEEN_3RD_PARTY_TUTORIAL"];
[[NSUserDefaults standardUserDefaults] synchronize];
[self onBack];
}
- (UIView *)createSlideWithData:(NSDictionary *)slide {
UIView *view = [[UIView alloc] init];
// Title
UILabel *titleLabel = [[UILabel alloc] init];
titleLabel.text = slide[@"title"];
titleLabel.font = [UIFont boldSystemFontOfSize:20];
titleLabel.textAlignment = NSTextAlignmentLeft; // left align
titleLabel.numberOfLines = 0;
titleLabel.translatesAutoresizingMaskIntoConstraints = NO;
// Subtitle
UILabel *subtitleLabel = [[UILabel alloc] init];
subtitleLabel.text = slide[@"subtitle"];
subtitleLabel.font = [UIFont systemFontOfSize:16];
subtitleLabel.textAlignment = NSTextAlignmentLeft; // left align
subtitleLabel.numberOfLines = 0;
subtitleLabel.translatesAutoresizingMaskIntoConstraints = NO;
// Image
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
UIImage *image = [UIImage imageNamed:slide[@"image"] inBundle:bundle compatibleWithTraitCollection:nil];
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
imageView.contentMode = UIViewContentModeScaleAspectFit;
imageView.translatesAutoresizingMaskIntoConstraints = NO;
[view addSubview:titleLabel];
[view addSubview:subtitleLabel];
[view addSubview:imageView];
UILayoutGuide *safe = view.safeAreaLayoutGuide;
[NSLayoutConstraint activateConstraints:@[
// Title at top
[titleLabel.topAnchor constraintEqualToAnchor:safe.topAnchor constant:20],
[titleLabel.leadingAnchor constraintEqualToAnchor:safe.leadingAnchor constant:16],
[titleLabel.trailingAnchor constraintEqualToAnchor:safe.trailingAnchor constant:-16],
// Subtitle under title
[subtitleLabel.topAnchor constraintEqualToAnchor:titleLabel.bottomAnchor constant:12],
[subtitleLabel.leadingAnchor constraintEqualToAnchor:safe.leadingAnchor constant:16],
[subtitleLabel.trailingAnchor constraintEqualToAnchor:safe.trailingAnchor constant:-16],
// Image under subtitle
[imageView.topAnchor constraintEqualToAnchor:subtitleLabel.bottomAnchor constant:20],
[imageView.leadingAnchor constraintEqualToAnchor:safe.leadingAnchor constant:16],
[imageView.trailingAnchor constraintEqualToAnchor:safe.trailingAnchor constant:-16],
[imageView.bottomAnchor constraintLessThanOrEqualToAnchor:safe.bottomAnchor constant:-20],
[imageView.heightAnchor constraintLessThanOrEqualToAnchor:safe.heightAnchor multiplier:0.5] // max 50% of screen
]];
return view;
}
- (void)setupPageControl {
self.pageControl = [[UIPageControl alloc] init];
self.pageControl.numberOfPages = self.slides.count;
self.pageControl.currentPage = 0;
self.pageControl.pageIndicatorTintColor = [UIColor lightGrayColor];
self.pageControl.currentPageIndicatorTintColor = [UIColor colorWithRed:0.05 green:0.45 blue:0.57 alpha:1.0]; // #0E7392
self.pageControl.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:self.pageControl];
UILayoutGuide *safe = self.view.safeAreaLayoutGuide;
[NSLayoutConstraint activateConstraints:@[
[self.pageControl.bottomAnchor constraintEqualToAnchor:safe.bottomAnchor constant:-60],
[self.pageControl.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor]
]];
}
- (void)setupNextButton {
self.nextButton = [UIButton buttonWithType:UIButtonTypeSystem];
[self.nextButton setTitle:@"Next" forState:UIControlStateNormal];
[self.nextButton setTitleColor:UIColor.whiteColor forState:UIControlStateNormal];
self.nextButton.backgroundColor = [UIColor colorWithRed:0.05 green:0.45 blue:0.57 alpha:1.0]; // #0E7392
self.nextButton.layer.cornerRadius = 20;
self.nextButton.translatesAutoresizingMaskIntoConstraints = NO;
[self.nextButton addTarget:self action:@selector(onNextPress) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:self.nextButton];
UILayoutGuide *safe = self.view.safeAreaLayoutGuide;
[NSLayoutConstraint activateConstraints:@[
[self.nextButton.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:-16],
[self.nextButton.bottomAnchor constraintEqualToAnchor:safe.bottomAnchor constant:-16],
[self.nextButton.widthAnchor constraintEqualToConstant:100],
[self.nextButton.heightAnchor constraintEqualToConstant:44]
]];
}
#pragma mark - helpers
- (void)onBack {
[self dismissViewControllerAnimated:YES completion:nil];
}
- (void)onSkip {
[self finishTutorial];
}
@end
......@@ -78,7 +78,16 @@ NS_ASSUME_NONNULL_BEGIN
completion:(void (^)(BOOL success, NSString *filePath))completion;
+ (void)requestCompanyCode:(NSString *)companyCode
completion:(void(^)(BOOL success, NSDictionary * _Nullable response, NSError * error))completion;
completion:(void(^)(BOOL success, NSDictionary * _Nullable response, NSError * error))completion;
+ (void)requestThirdPartyList:(void(^)(BOOL success, NSDictionary * _Nullable response, NSError * _Nullable error))completion;
+ (void)createThirdPartyLink:(NSString *)title
lotId:(NSString *)lotId
completion:(void(^)(BOOL success, NSDictionary * _Nullable response, NSError * _Nullable error))completion;
+ (void)updateThirdPartyLink:(NSDictionary *)payload
completion:(void(^)(NSDictionary * _Nullable response,NSError * _Nullable error))completion;
@end
......
......@@ -1950,6 +1950,185 @@
[task resume];
}
+ (void)requestThirdPartyList:(void(^)(BOOL success, NSDictionary * _Nullable response, NSError * _Nullable error))completion {
if ([APIConfig handleOfflineForAPI:@"ThirdPartyList" completion:completion]) {
return;
}
NSURL *url = [APIConfig urlWithPath:@"/framework/third-party/auth/listCode"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST";
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
[request setValue:@"application/json" forHTTPHeaderField:@"Accept"];
NSLog(@"🌍 Request URL: %@", url.absoluteString);
NSError *jsonError;
if (jsonError) {
NSLog(@"❌ JSON Serialization Error: %@", jsonError);
if (completion) completion(NO, nil, jsonError);
return;
}
NSLog(@"🔄 Request thirdPartyList started...");
NSURLSessionDataTask *task = [[NSURLSession sharedSession]
dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (error) {
NSLog(@"❌ Network Error: %@", error);
dispatch_async(dispatch_get_main_queue(), ^{
completion(NO, nil, error);
});
return;
}
NSError *parseError;
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];
if (parseError) {
NSLog(@"⚠️ JSON Parse Error: %@", parseError);
dispatch_async(dispatch_get_main_queue(), ^{
completion(NO, nil, parseError);
});
return;
}
NSString *cacheKey = [NSString stringWithFormat:@"AccessItem_%@", [APIConfig projectId]];
NSDictionary *safeJson = [APIConfig dictionaryByReplacingNullsWithBlanks:json];
[LocalStorage saveDictionary:safeJson forKey:cacheKey];
NSLog(@"💾 Saved to cache: %@", cacheKey);
dispatch_async(dispatch_get_main_queue(), ^{
completion(YES, json, nil);
});
}];
[task resume];
}
+ (void)createThirdPartyLink:(NSString *)title
lotId:(NSString *)lotId
completion:(void(^)(BOOL success, NSDictionary * _Nullable response, NSError * _Nullable error))completion {
NSURL *url = [APIConfig urlWithPath:@"/framework/third-party/auth/generateCode"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST";
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
[request setValue:@"application/json" forHTTPHeaderField:@"Accept"];
NSDictionary *jsonBody = @{
@"title": title ?: @"",
@"unit_id": lotId ?: @"",
@"status": @1,
@"created_by": [APIConfig clientId],
};
// ✅ Convert dictionary → NSData
NSError *jsonError;
NSData *bodyData = [NSJSONSerialization dataWithJSONObject:jsonBody options:0 error:&jsonError];
if (jsonError) {
NSLog(@"❌ JSON Serialization Error: %@", jsonError);
if (completion) completion(NO, nil, jsonError);
return;
}
request.HTTPBody = bodyData;
// 🧭 Debug logs
NSLog(@"🌍 URL: %@", url);
NSLog(@"📦 Body JSON: %@", jsonBody);
// ✅ Make request
NSURLSessionDataTask *task = [[NSURLSession sharedSession]
dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (error) {
NSLog(@"❌ Network Error: %@", error);
dispatch_async(dispatch_get_main_queue(), ^{
if (completion) completion(NO, nil, error);
});
return;
}
NSError *jsonParseError;
NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonParseError];
if (jsonParseError) {
NSLog(@"⚠️ JSON Parse Error: %@", jsonParseError);
} else {
NSLog(@"✅ Create Third Party Link Response:\n%@", jsonResponse);
}
dispatch_async(dispatch_get_main_queue(), ^{
if (completion) {
completion(YES, jsonResponse, jsonParseError);
}
});
}];
[task resume];
}
+ (void)updateThirdPartyLink:(NSDictionary *)payload
completion:(void(^)(NSDictionary * _Nullable response,NSError * _Nullable error))completion {
NSURL *url = [APIConfig urlWithPath:@"/framework/third-party/auth/update"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST";
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
[request setValue:@"application/json" forHTTPHeaderField:@"Accept"];
NSError *jsonError;
NSData *bodyData =
[NSJSONSerialization dataWithJSONObject:payload
options:0
error:&jsonError];
if (jsonError) {
if (completion) completion(nil, jsonError);
return;
}
request.HTTPBody = bodyData;
NSLog(@"🌍 URL: %@", url);
NSLog(@"📦 Body JSON: %@", payload);
NSURLSessionDataTask *task =
[[NSURLSession sharedSession] dataTaskWithRequest:request
completionHandler:^(NSData *data,
NSURLResponse *response,
NSError *error) {
if (error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (completion) completion(nil, error);
});
return;
}
NSError *parseError;
NSDictionary *jsonResponse =
[NSJSONSerialization JSONObjectWithData:data
options:0
error:&parseError];
dispatch_async(dispatch_get_main_queue(), ^{
if (completion) {
completion(jsonResponse, parseError);
}
});
}];
[task resume];
}
#pragma mark - Helper Methods
+ (NSString *)multipartFormField:(NSString *)name value:(NSString *)value boundary:(NSString *)boundary {
return [NSString stringWithFormat:@"--%@\r\nContent-Disposition: form-data; name=\"data[%@]\"\r\n\r\n%@\r\n", boundary, name, value];
......
......@@ -7,7 +7,7 @@ static NSString *kBaseURL = @"https://sptest.commudesk.com/api";
static NSString *kDrawingPlanId = @"";
static NSString *kProjectId = @"";
static NSString *kAuthToken = @"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzY29wZSI6InNlcnZlci10by1zZXJ2ZXIiLCJmcmFtZXdvcmstYXBpIjp0cnVlLCJjb21wYW55X2NvZGUiOiJTUFQiLCJzdWIiOjE1NTcsImlzcyI6Imh0dHBzOi8vc3B0ZXN0LmNvbW11ZGVzay5jb20vYXBpL2ZyYW1ld29yay9vd25lci9hdXRoL2NsaWVudF9hdXRoZW50aWNhdGlvbiIsImlhdCI6MTc2Nzc1MjU5OSwiZXhwIjoyMDgzMzIyMTE5LCJuYmYiOjE3Njc3NTI1OTksImp0aSI6IktEcmhmSXgzeWU0WmxaUEYifQ.Bv4ZSjGRm9YYOjGItLSV_3wS7grKuPTabL60BFnp88o";
static NSString *kClientId = @"3";
static NSString *kClientId = @"1";
@implementation APIConfig
#pragma mark - OS
......
......@@ -6,7 +6,7 @@
<dict>
<key>Assets.car</key>
<data>
apewM0kg8bkb0nA0BqxaEqt0L8I=
1i2tuf6x04oClRQdI25h80iAWww=
</data>
<key>Headers/QmsPlugin.h</key>
<data>
......@@ -31,7 +31,7 @@
<dict>
<key>hash2</key>
<data>
kujF94lp6x0Gv/ur/rYZEuwSRGB/AosKCC73EYwiL8g=
1mZEAflZoNOaTXIhGRR7gPGBZ99fx2jFZ147Fkro4WM=
</data>
</dict>
<key>Headers/QmsPlugin.h</key>
......
......@@ -6,7 +6,7 @@
<dict>
<key>Assets.car</key>
<data>
apewM0kg8bkb0nA0BqxaEqt0L8I=
1i2tuf6x04oClRQdI25h80iAWww=
</data>
<key>Headers/QmsPlugin.h</key>
<data>
......@@ -31,7 +31,7 @@
<dict>
<key>hash2</key>
<data>
kujF94lp6x0Gv/ur/rYZEuwSRGB/AosKCC73EYwiL8g=
1mZEAflZoNOaTXIhGRR7gPGBZ99fx2jFZ147Fkro4WM=
</data>
</dict>
<key>Headers/QmsPlugin.h</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