Commit 87014f12 authored by Wei Han's avatar Wei Han

code update

parent 09b0eb2e
...@@ -7,9 +7,9 @@ ...@@ -7,9 +7,9 @@
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
09422FEEE292BF7FDB944B44 /* libPods-example.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7646DEA673059A6FBED034D5 /* libPods-example.a */; };
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
44006126336D83DB220E7214 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */; }; 44006126336D83DB220E7214 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */; };
705C78A9CBF3D37B370DC92C /* libPods-example.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CB77023451E7FACEB5CCC67A /* libPods-example.a */; };
761780ED2CA45674006654EE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 761780EC2CA45674006654EE /* AppDelegate.swift */; }; 761780ED2CA45674006654EE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 761780EC2CA45674006654EE /* AppDelegate.swift */; };
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; }; 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
...@@ -19,11 +19,11 @@ ...@@ -19,11 +19,11 @@
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = example/Images.xcassets; sourceTree = "<group>"; }; 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = example/Images.xcassets; sourceTree = "<group>"; };
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = example/Info.plist; sourceTree = "<group>"; }; 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = example/Info.plist; sourceTree = "<group>"; };
13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = PrivacyInfo.xcprivacy; path = example/PrivacyInfo.xcprivacy; sourceTree = "<group>"; }; 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = PrivacyInfo.xcprivacy; path = example/PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
31E3569C4DACA1583750BB4B /* Pods-example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example.debug.xcconfig"; path = "Target Support Files/Pods-example/Pods-example.debug.xcconfig"; sourceTree = "<group>"; }; 5350E2781CCDA8D204AD92D7 /* Pods-example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example.debug.xcconfig"; path = "Target Support Files/Pods-example/Pods-example.debug.xcconfig"; sourceTree = "<group>"; };
67CF26DD341753F546BD183F /* Pods-example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example.release.xcconfig"; path = "Target Support Files/Pods-example/Pods-example.release.xcconfig"; sourceTree = "<group>"; }; 66DA0C9F0E73892891015312 /* Pods-example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example.release.xcconfig"; path = "Target Support Files/Pods-example/Pods-example.release.xcconfig"; sourceTree = "<group>"; };
761780EC2CA45674006654EE /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = example/AppDelegate.swift; sourceTree = "<group>"; }; 761780EC2CA45674006654EE /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = example/AppDelegate.swift; sourceTree = "<group>"; };
7646DEA673059A6FBED034D5 /* libPods-example.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-example.a"; sourceTree = BUILT_PRODUCTS_DIR; };
81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = example/LaunchScreen.storyboard; sourceTree = "<group>"; }; 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = example/LaunchScreen.storyboard; sourceTree = "<group>"; };
CB77023451E7FACEB5CCC67A /* libPods-example.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-example.a"; sourceTree = BUILT_PRODUCTS_DIR; };
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
/* End PBXFileReference section */ /* End PBXFileReference section */
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
705C78A9CBF3D37B370DC92C /* libPods-example.a in Frameworks */, 09422FEEE292BF7FDB944B44 /* libPods-example.a in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
...@@ -55,7 +55,7 @@ ...@@ -55,7 +55,7 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
ED297162215061F000B7C4FE /* JavaScriptCore.framework */, ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
CB77023451E7FACEB5CCC67A /* libPods-example.a */, 7646DEA673059A6FBED034D5 /* libPods-example.a */,
); );
name = Frameworks; name = Frameworks;
sourceTree = "<group>"; sourceTree = "<group>";
...@@ -92,8 +92,8 @@ ...@@ -92,8 +92,8 @@
BBD78D7AC51CEA395F1C20DB /* Pods */ = { BBD78D7AC51CEA395F1C20DB /* Pods */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
31E3569C4DACA1583750BB4B /* Pods-example.debug.xcconfig */, 5350E2781CCDA8D204AD92D7 /* Pods-example.debug.xcconfig */,
67CF26DD341753F546BD183F /* Pods-example.release.xcconfig */, 66DA0C9F0E73892891015312 /* Pods-example.release.xcconfig */,
); );
path = Pods; path = Pods;
sourceTree = "<group>"; sourceTree = "<group>";
...@@ -105,13 +105,13 @@ ...@@ -105,13 +105,13 @@
isa = PBXNativeTarget; isa = PBXNativeTarget;
buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "example" */; buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "example" */;
buildPhases = ( buildPhases = (
0C0B6164CF1596DDFC4F19C5 /* [CP] Check Pods Manifest.lock */, 1A63BC39C4970FBC7B74189D /* [CP] Check Pods Manifest.lock */,
13B07F871A680F5B00A75B9A /* Sources */, 13B07F871A680F5B00A75B9A /* Sources */,
13B07F8C1A680F5B00A75B9A /* Frameworks */, 13B07F8C1A680F5B00A75B9A /* Frameworks */,
13B07F8E1A680F5B00A75B9A /* Resources */, 13B07F8E1A680F5B00A75B9A /* Resources */,
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
1784E64423995B1E7F285F50 /* [CP] Embed Pods Frameworks */, 4C643B981BE7FEF41566F449 /* [CP] Embed Pods Frameworks */,
6A78F44E33668E0CBF927584 /* [CP] Copy Pods Resources */, 4DB8EF54BF2FC8DAD832C9D8 /* [CP] Copy Pods Resources */,
); );
buildRules = ( buildRules = (
); );
...@@ -183,7 +183,7 @@ ...@@ -183,7 +183,7 @@
shellPath = /bin/sh; shellPath = /bin/sh;
shellScript = "set -e\n\nWITH_ENVIRONMENT=\"$REACT_NATIVE_PATH/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"$REACT_NATIVE_PATH/scripts/react-native-xcode.sh\"\n\n/bin/sh -c \"$WITH_ENVIRONMENT $REACT_NATIVE_XCODE\"\n"; shellScript = "set -e\n\nWITH_ENVIRONMENT=\"$REACT_NATIVE_PATH/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"$REACT_NATIVE_PATH/scripts/react-native-xcode.sh\"\n\n/bin/sh -c \"$WITH_ENVIRONMENT $REACT_NATIVE_XCODE\"\n";
}; };
0C0B6164CF1596DDFC4F19C5 /* [CP] Check Pods Manifest.lock */ = { 1A63BC39C4970FBC7B74189D /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
...@@ -205,7 +205,7 @@ ...@@ -205,7 +205,7 @@
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0; showEnvVarsInLog = 0;
}; };
1784E64423995B1E7F285F50 /* [CP] Embed Pods Frameworks */ = { 4C643B981BE7FEF41566F449 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
...@@ -222,7 +222,7 @@ ...@@ -222,7 +222,7 @@
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-frameworks.sh\"\n"; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-frameworks.sh\"\n";
showEnvVarsInLog = 0; showEnvVarsInLog = 0;
}; };
6A78F44E33668E0CBF927584 /* [CP] Copy Pods Resources */ = { 4DB8EF54BF2FC8DAD832C9D8 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
...@@ -255,7 +255,7 @@ ...@@ -255,7 +255,7 @@
/* Begin XCBuildConfiguration section */ /* Begin XCBuildConfiguration section */
13B07F941A680F5B00A75B9A /* Debug */ = { 13B07F941A680F5B00A75B9A /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = 31E3569C4DACA1583750BB4B /* Pods-example.debug.xcconfig */; baseConfigurationReference = 5350E2781CCDA8D204AD92D7 /* Pods-example.debug.xcconfig */;
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
...@@ -283,7 +283,7 @@ ...@@ -283,7 +283,7 @@
}; };
13B07F951A680F5B00A75B9A /* Release */ = { 13B07F951A680F5B00A75B9A /* Release */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = 67CF26DD341753F546BD183F /* Pods-example.release.xcconfig */; baseConfigurationReference = 66DA0C9F0E73892891015312 /* Pods-example.release.xcconfig */;
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
......
...@@ -632,15 +632,15 @@ ...@@ -632,15 +632,15 @@
_currentY = 16.0; _currentY = 16.0;
// === PROJECT UPDATES === // === PROJECT UPDATES ===
NSArray *announcements = self.announcements; // NSArray *announcements = self.announcements;
if (announcements.count > 0) { // if (announcements.count > 0) {
UIView *announcementSection = [self buildAnnouncementSection:announcements]; // UIView *announcementSection = [self buildAnnouncementSection:announcements];
CGRect f = announcementSection.frame; // CGRect f = announcementSection.frame;
f.origin.y = _currentY; // f.origin.y = _currentY;
announcementSection.frame = f; // announcementSection.frame = f;
[_contentContainer addSubview:announcementSection]; // [_contentContainer addSubview:announcementSection];
_currentY += CGRectGetHeight(announcementSection.frame) + 12; // _currentY += CGRectGetHeight(announcementSection.frame) + 12;
} // }
// === APPOINTMENTS === // === APPOINTMENTS ===
NSArray *appointments = mainData[@"appointment"]; NSArray *appointments = mainData[@"appointment"];
...@@ -668,7 +668,8 @@ ...@@ -668,7 +668,8 @@
_scrollView.contentSize = CGSizeMake(_scrollView.bounds.size.width, _currentY); _scrollView.contentSize = CGSizeMake(_scrollView.bounds.size.width, _currentY);
NSLog(@"✅ Dashboard UI refreshed with %lu announcements, %lu appointments, %lu issues", NSLog(@"✅ Dashboard UI refreshed with %lu announcements, %lu appointments, %lu issues",
(unsigned long)announcements.count, (unsigned long)appointments.count, (unsigned long)issues.count); // (unsigned long)announcements.count,
(unsigned long)appointments.count, (unsigned long)issues.count);
} }
- (UIView *)buildAnnouncementSection:(NSArray *)announcements { - (UIView *)buildAnnouncementSection:(NSArray *)announcements {
......
...@@ -84,7 +84,7 @@ NSString * const DrawerDidSelectItemNotification = @"DrawerDidSelectItemNotifica ...@@ -84,7 +84,7 @@ NSString * const DrawerDidSelectItemNotification = @"DrawerDidSelectItemNotifica
[self.drawerVC didMoveToParentViewController:self]; [self.drawerVC didMoveToParentViewController:self];
self.drawerLeadingConstraint = self.drawerLeadingConstraint =
[self.drawerVC.view.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:-self.drawerWidth]; [self.drawerVC.view.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:self.drawerWidth];
[self.drawerVC.view.widthAnchor constraintEqualToConstant:self.drawerWidth].active = YES; [self.drawerVC.view.widthAnchor constraintEqualToConstant:self.drawerWidth].active = YES;
[self.drawerVC.view.topAnchor constraintEqualToAnchor:self.view.topAnchor].active = YES; [self.drawerVC.view.topAnchor constraintEqualToAnchor:self.view.topAnchor].active = YES;
...@@ -123,11 +123,10 @@ NSString * const DrawerDidSelectItemNotification = @"DrawerDidSelectItemNotifica ...@@ -123,11 +123,10 @@ NSString * const DrawerDidSelectItemNotification = @"DrawerDidSelectItemNotifica
#pragma mark - Drawer Actions #pragma mark - Drawer Actions
- (void)openDrawer { - (void)openDrawer {
// Make sure overlay and drawer are on top
[self.view bringSubviewToFront:self.overlayView]; [self.view bringSubviewToFront:self.overlayView];
[self.view bringSubviewToFront:self.drawerVC.view]; [self.view bringSubviewToFront:self.drawerVC.view];
self.drawerLeadingConstraint.constant = 0; self.drawerLeadingConstraint.constant = 0; // moves drawer fully into view
[UIView animateWithDuration:0.25 animations:^{ [UIView animateWithDuration:0.25 animations:^{
[self.view layoutIfNeeded]; [self.view layoutIfNeeded];
...@@ -138,7 +137,7 @@ NSString * const DrawerDidSelectItemNotification = @"DrawerDidSelectItemNotifica ...@@ -138,7 +137,7 @@ NSString * const DrawerDidSelectItemNotification = @"DrawerDidSelectItemNotifica
} }
- (void)closeDrawer { - (void)closeDrawer {
self.drawerLeadingConstraint.constant = -self.drawerWidth; self.drawerLeadingConstraint.constant = self.drawerWidth; // moves drawer offscreen
[UIView animateWithDuration:0.25 animations:^{ [UIView animateWithDuration:0.25 animations:^{
[self.view layoutIfNeeded]; [self.view layoutIfNeeded];
...@@ -167,12 +166,13 @@ NSString * const DrawerDidSelectItemNotification = @"DrawerDidSelectItemNotifica ...@@ -167,12 +166,13 @@ NSString * const DrawerDidSelectItemNotification = @"DrawerDidSelectItemNotifica
startX = self.mainVC.view.transform.tx; startX = self.mainVC.view.transform.tx;
} else if (pan.state == UIGestureRecognizerStateChanged) { } else if (pan.state == UIGestureRecognizerStateChanged) {
CGFloat newTx = startX + x; CGFloat newTx = startX + x;
newTx = MAX(0, newTx); // Clamp between 0 (open) and -drawerWidth (closed)
newTx = MIN(self.drawerWidth, newTx); newTx = MIN(0, newTx);
newTx = MAX(-self.drawerWidth, newTx);
CGFloat progress = newTx / self.drawerWidth; CGFloat progress = fabs(newTx / self.drawerWidth);
self.mainVC.view.transform = CGAffineTransformMakeTranslation(newTx, 0); self.mainVC.view.transform = CGAffineTransformMakeTranslation(newTx, 0);
self.drawerLeadingConstraint.constant = -self.drawerWidth + newTx; self.drawerLeadingConstraint.constant = newTx; // trailing anchor moves from +drawerWidth → 0
[self.view layoutIfNeeded]; [self.view layoutIfNeeded];
self.overlayView.alpha = progress; self.overlayView.alpha = progress;
} else if (pan.state == UIGestureRecognizerStateEnded || pan.state == UIGestureRecognizerStateCancelled) { } else if (pan.state == UIGestureRecognizerStateEnded || pan.state == UIGestureRecognizerStateCancelled) {
...@@ -181,9 +181,9 @@ NSString * const DrawerDidSelectItemNotification = @"DrawerDidSelectItemNotifica ...@@ -181,9 +181,9 @@ NSString * const DrawerDidSelectItemNotification = @"DrawerDidSelectItemNotifica
BOOL shouldOpen = NO; BOOL shouldOpen = NO;
if (fabs(velocityX) > 400) { if (fabs(velocityX) > 400) {
shouldOpen = (velocityX > 0); shouldOpen = (velocityX < 0); // left swipe opens drawer
} else { } else {
shouldOpen = currentTx > (self.drawerWidth * 0.45); shouldOpen = fabs(currentTx) > (self.drawerWidth * 0.45);
} }
if (shouldOpen) [self openDrawer]; if (shouldOpen) [self openDrawer];
......
// ProjectViewController.h
#import <UIKit/UIKit.h>
#import "DrawerController.h"
@interface ProjectViewController : UIViewController
@property (nonatomic, weak) DrawerController *drawerController;
@property (nonatomic, copy) NSString *ClientID;
@property (nonatomic, copy) NSString *ClientCode;
@property (nonatomic, copy) NSString *user_token;
@end
// ProjectViewController.mm
#import "ProjectViewController.h"
@interface ProjectViewController ()
@property (nonatomic, strong) UIView *headerView;
@property (nonatomic, strong) UIButton *backButton;
@property (nonatomic, strong) UILabel *titleLabel;
@end
@implementation ProjectViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = UIColor.systemBackgroundColor;
[self setupHeader];
}
#pragma mark - Header Setup
- (void)setupHeader {
self.headerView = [[UIView alloc] init];
self.headerView.backgroundColor = [UIColor colorWithRed:0.0 green:0.5 blue:0.6 alpha:1.0];
self.headerView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:self.headerView];
UIButton *menuButton = [UIButton buttonWithType:UIButtonTypeCustom];
menuButton.translatesAutoresizingMaskIntoConstraints = NO;
[menuButton setImage:[UIImage systemImageNamed:@"line.horizontal.3"] forState:UIControlStateNormal];
menuButton.tintColor = UIColor.whiteColor;
[menuButton addTarget:self action:@selector(handleMenuButtonTapped) forControlEvents:UIControlEventTouchUpInside];
[self.headerView addSubview:menuButton];
[NSLayoutConstraint activateConstraints:@[
[self.headerView.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor],
[self.headerView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
[self.headerView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
[self.headerView.heightAnchor constraintEqualToConstant:48],
[menuButton.trailingAnchor constraintEqualToAnchor:self.headerView.trailingAnchor constant:-16],
[menuButton.centerYAnchor constraintEqualToAnchor:self.headerView.centerYAnchor],
[menuButton.widthAnchor constraintEqualToConstant:26],
[menuButton.heightAnchor constraintEqualToConstant:26],
[self.titleLabel.centerXAnchor constraintEqualToAnchor:self.headerView.centerXAnchor],
[self.titleLabel.centerYAnchor constraintEqualToAnchor:self.headerView.centerYAnchor],
]];
}
#pragma mark - Helper
- (void)handleMenuButtonTapped {
NSLog(@"🍔 Menu button tapped");
if (self.drawerController) {
[self.drawerController toggleDrawer];
} else {
NSLog(@"⚠️ drawerController not set on DashboardViewController");
}
}
@end
// UnitViewController.h
#import <UIKit/UIKit.h>
#import "DrawerController.h"
@interface UnitViewController : UIViewController
@property (nonatomic, weak) DrawerController *drawerController;
@property (nonatomic, copy) NSString *ClientID;
@property (nonatomic, copy) NSString *ClientCode;
@property (nonatomic, copy) NSString *user_token;
@end
// UnitViewController.mm
#import "UnitViewController.h"
@interface UnitViewController ()
@property (nonatomic, strong) UIView *headerView;
@property (nonatomic, strong) UIButton *backButton;
@property (nonatomic, strong) UILabel *titleLabel;
@end
@implementation UnitViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = UIColor.systemBackgroundColor;
[self setupHeader];
}
#pragma mark - Header Setup
- (void)setupHeader {
self.headerView = [[UIView alloc] init];
self.headerView.backgroundColor = [UIColor colorWithRed:0.0 green:0.5 blue:0.6 alpha:1.0];
self.headerView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:self.headerView];
self.backButton = [UIButton buttonWithType:UIButtonTypeCustom];
[self.backButton setImage:[UIImage systemImageNamed:@"chevron.backward"]
forState:UIControlStateNormal];
self.backButton.tintColor = UIColor.whiteColor;
[self.backButton addTarget:self
action:@selector(onBack)
forControlEvents:UIControlEventTouchUpInside];
self.backButton.translatesAutoresizingMaskIntoConstraints = NO;
[self.headerView addSubview:self.backButton];
self.titleLabel = [[UILabel alloc] init];
self.titleLabel.text = @"Unit";
self.titleLabel.textColor = UIColor.whiteColor;
self.titleLabel.font = [UIFont boldSystemFontOfSize:18];
self.titleLabel.translatesAutoresizingMaskIntoConstraints = NO;
[self.headerView addSubview:self.titleLabel];
UIButton *menuButton = [UIButton buttonWithType:UIButtonTypeCustom];
menuButton.translatesAutoresizingMaskIntoConstraints = NO;
[menuButton setImage:[UIImage systemImageNamed:@"line.horizontal.3"] forState:UIControlStateNormal];
menuButton.tintColor = UIColor.whiteColor;
[menuButton addTarget:self action:@selector(handleMenuButtonTapped) forControlEvents:UIControlEventTouchUpInside];
[self.headerView addSubview:menuButton];
[NSLayoutConstraint activateConstraints:@[
[self.headerView.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor],
[self.headerView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
[self.headerView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
[self.headerView.heightAnchor constraintEqualToConstant:48],
[self.backButton.leadingAnchor constraintEqualToAnchor:self.headerView.leadingAnchor constant:8],
[self.backButton.centerYAnchor constraintEqualToAnchor:self.headerView.centerYAnchor],
[self.backButton.widthAnchor constraintEqualToConstant:32],
[self.backButton.heightAnchor constraintEqualToConstant:32],
[menuButton.trailingAnchor constraintEqualToAnchor:self.headerView.trailingAnchor constant:-16],
[menuButton.centerYAnchor constraintEqualToAnchor:self.headerView.centerYAnchor],
[menuButton.widthAnchor constraintEqualToConstant:26],
[menuButton.heightAnchor constraintEqualToConstant:26],
[self.titleLabel.centerXAnchor constraintEqualToAnchor:self.headerView.centerXAnchor],
[self.titleLabel.centerYAnchor constraintEqualToAnchor:self.headerView.centerYAnchor],
]];
}
#pragma mark - Helper
- (void)onBack {
[self dismissViewControllerAnimated:YES completion:nil];
}
- (void)handleMenuButtonTapped {
NSLog(@"🍔 Menu button tapped");
if (self.drawerController) {
[self.drawerController toggleDrawer];
} else {
NSLog(@"⚠️ drawerController not set on DashboardViewController");
}
}
@end
...@@ -4,35 +4,45 @@ ...@@ -4,35 +4,45 @@
#import "DrawerViewController.h" #import "DrawerViewController.h"
#import "DrawerController.h" #import "DrawerController.h"
#import "NetworkManager.h" #import "NetworkManager.h"
#import "UnitViewController.h"
#import "ProjectViewController.h"
@implementation QmsPluginUI @implementation QmsPluginUI
+ (UIViewController *)makeViewController { + (UIViewController *)makeViewController {
// 0. Start network monitoring immediately
[NetworkManager sharedManager]; [NetworkManager sharedManager];
// 1. Create drawer // 1️⃣ Drawer menu
DrawerViewController *drawer = [[DrawerViewController alloc] init]; DrawerViewController *drawer = [[DrawerViewController alloc] init];
drawer.profileName = @"placeholder"; drawer.profileName = @"placeholder";
drawer.profileEmail = @"placeholder"; drawer.profileEmail = @"placeholder";
// 2. Client props // 2️⃣ Screens
ProjectViewController *project = [[ProjectViewController alloc] init]; // Landing page
UnitViewController *unit = [[UnitViewController alloc] init]; // Secondary page
DashboardViewController *dashboard = [[DashboardViewController alloc] init];
// 3️⃣ UINavigationController with landing page
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:project];
// 4️⃣ Create drawer controller wrapping nav
NSString *clientID = @"clientID_loading"; NSString *clientID = @"clientID_loading";
NSString *clientCode = @"clientCode_loading"; NSString *clientCode = @"clientCode_loading";
NSString *userToken = @"userToken_loading"; NSString *userToken = @"userToken_loading";
// 3. Root container DrawerController *drawerController = [[DrawerController alloc]
DrawerController *drawerController = initWithDrawerViewController:drawer
[[DrawerController alloc] initWithDrawerViewController:drawer
clientID:clientID clientID:clientID
clientCode:clientCode clientCode:clientCode
userToken:userToken]; userToken:userToken];
// 4. Back-reference // 5️⃣ Inject drawerController reference into all screens
if ([drawerController.mainViewController isKindOfClass:[DashboardViewController class]]) { project.drawerController = drawerController;
((DashboardViewController *)drawerController.mainViewController).drawerController = unit.drawerController = drawerController;
drawerController; dashboard.drawerController = drawerController;
}
// 6️⃣ Replace mainVC in DrawerController with nav
[drawerController setValue:nav forKey:@"_mainVC"]; // or update your property to accept UINavigationController
return drawerController; return drawerController;
} }
......
...@@ -68,6 +68,7 @@ ...@@ -68,6 +68,7 @@
#pragma mark - Detail Content #pragma mark - Detail Content
- (void)setupDetailContent { - (void)setupDetailContent {
// 1️⃣ Scroll view container
UIScrollView *scroll = [[UIScrollView alloc] init]; UIScrollView *scroll = [[UIScrollView alloc] init];
scroll.translatesAutoresizingMaskIntoConstraints = NO; scroll.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:scroll]; [self.view addSubview:scroll];
...@@ -89,6 +90,7 @@ ...@@ -89,6 +90,7 @@
[container.widthAnchor constraintEqualToAnchor:scroll.widthAnchor], [container.widthAnchor constraintEqualToAnchor:scroll.widthAnchor],
]]; ]];
// 2️⃣ Stack view
UIStackView *stack = [[UIStackView alloc] init]; UIStackView *stack = [[UIStackView alloc] init];
stack.axis = UILayoutConstraintAxisVertical; stack.axis = UILayoutConstraintAxisVertical;
stack.spacing = 12; stack.spacing = 12;
...@@ -102,35 +104,48 @@ ...@@ -102,35 +104,48 @@
[stack.bottomAnchor constraintLessThanOrEqualToAnchor:container.bottomAnchor constant:-16], [stack.bottomAnchor constraintLessThanOrEqualToAnchor:container.bottomAnchor constant:-16],
]]; ]];
// 3️⃣ Unit label
UILabel *unitLabel = [[UILabel alloc] init]; UILabel *unitLabel = [[UILabel alloc] init];
unitLabel.text = self.itemData[@"unitName"]; unitLabel.text = self.itemData[@"unitName"] ?: @"-";
unitLabel.font = [UIFont boldSystemFontOfSize:22]; unitLabel.font = [UIFont boldSystemFontOfSize:22];
unitLabel.textColor = UIColor.labelColor; unitLabel.textColor = UIColor.labelColor;
unitLabel.numberOfLines = 0; unitLabel.numberOfLines = 0;
[stack addArrangedSubview:unitLabel]; [stack addArrangedSubview:unitLabel];
// spacing // 4️⃣ Spacer
UIView *spacer = [[UIView alloc] init]; UIView *spacer = [[UIView alloc] init];
[spacer.heightAnchor constraintEqualToConstant:12].active = YES; [spacer.heightAnchor constraintEqualToConstant:12].active = YES;
[stack addArrangedSubview:spacer]; [stack addArrangedSubview:spacer];
// 5️⃣ Status color
NSString *status = self.itemData[@"status"]; NSString *status = self.itemData[@"status"];
UIColor *statusColor = [self colorForStatus:status]; UIColor *statusColor = [self colorForStatus:status];
// Add rows // 6️⃣ Helper block to safely get field values
[stack addArrangedSubview:[self makeRow:@"Created Date" value:self.itemData[@"createdOn"] color:nil]]; NSString *(^safeField)(NSString *) = ^NSString *(NSString *fieldName) {
[stack addArrangedSubview:[self makeRow:@"Created By" value:self.itemData[@"createdBy"] color:nil]]; id fieldNames = self.itemData[@"raw"][@"fieldNames"];
[stack addArrangedSubview:[self makeRow:@"Uploaded On" value:self.itemData[@"raw"][@"syncedAt"] color:statusColor]]; if ([fieldNames isKindOfClass:[NSDictionary class]]) {
[stack addArrangedSubview:[self makeRow:@"Action Made" value:self.itemData[@"type"] color:nil]]; id value = fieldNames[fieldName];
[stack addArrangedSubview:[self makeRow:@"Location" value:self.itemData[@"raw"][@"fieldNames"][@"locationName"] ?: @"-" color:nil]]; if ([value isKindOfClass:[NSString class]]) {
[stack addArrangedSubview:[self makeRow:@"Category" value:self.itemData[@"raw"][@"category"][@"categoryName"] ?: @"-" color:nil]]; return value;
[stack addArrangedSubview:[self makeRow:@"Type" value:self.itemData[@"raw"][@"type"][@"typeName"] color:nil]]; }
[stack addArrangedSubview:[self makeRow:@"Issue" value:self.itemData[@"raw"][@"issueDisplay"][@"issueName"] ?: @"-" color:nil]]; }
[stack addArrangedSubview:[self makeRow:@"Comment" value:self.itemData[@"raw"][@"remarks"] ?: @"-" color:nil]]; return @"-";
};
// 7️⃣ Add rows
[stack addArrangedSubview:[self makeRow:@"Created Date" value:self.itemData[@"createdOn"] ?: @"-" color:nil]];
[stack addArrangedSubview:[self makeRow:@"Created By" value:self.itemData[@"createdBy"] ?: @"-" color:nil]];
[stack addArrangedSubview:[self makeRow:@"Uploaded On" value:self.itemData[@"raw"][@"syncedAt"] ?: @"-" color:statusColor]];
[stack addArrangedSubview:[self makeRow:@"Action Made" value:self.itemData[@"type"] ?: @"-" color:nil]];
[stack addArrangedSubview:[self makeRow:@"Location" value:safeField(@"locationName") color:nil]];
[stack addArrangedSubview:[self makeRow:@"Category" value:safeField(@"categoryName") color:nil]];
[stack addArrangedSubview:[self makeRow:@"Type" value:safeField(@"typeName") color:nil]];
[stack addArrangedSubview:[self makeRow:@"Issue" value:safeField(@"issueName") color:nil]];
[stack addArrangedSubview:[self makeRow:@"Comment" value:self.itemData[@"raw"][@"remarks"] ?: self.itemData[@"raw"][@"payload"][@"comment"] ?: @"-" color:nil]];
} }
- (UIView *)makeRow:(NSString *)title - (UIView *)makeRow:(NSString *)title
value:(NSString *)value value:(NSString *)value
color:(UIColor *)color { color:(UIColor *)color {
......
...@@ -392,12 +392,17 @@ typedef NS_ENUM(NSInteger, HistorySyncStatus) { ...@@ -392,12 +392,17 @@ typedef NS_ENUM(NSInteger, HistorySyncStatus) {
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:YES]; [tableView deselectRowAtIndexPath:indexPath animated:YES];
NSLog(@"Tapped section %ld row %ld", (long)indexPath.section, (long)indexPath.row);
HistorySyncItem *selectedItem = self.filteredItems[indexPath.row]; HistorySyncItem *selectedItem = self.filteredItems[indexPath.row];
NSDictionary *safeData = [self normalizedRow:selectedItem.raw];
if (!safeData) {
NSLog(@"❌ Invalid history item tapped: %@", selectedItem.raw);
return;
}
HistoryDetailsViewController *vc = [HistoryDetailsViewController new]; HistoryDetailsViewController *vc = [HistoryDetailsViewController new];
vc.itemData = selectedItem.raw; vc.itemData = safeData;
vc.modalPresentationStyle = UIModalPresentationFullScreen; vc.modalPresentationStyle = UIModalPresentationFullScreen;
[self presentViewController:vc animated:YES completion:nil]; [self presentViewController:vc animated:YES completion:nil];
} }
...@@ -450,7 +455,7 @@ typedef NS_ENUM(NSInteger, HistorySyncStatus) { ...@@ -450,7 +455,7 @@ typedef NS_ENUM(NSInteger, HistorySyncStatus) {
[self.syncAgainButton.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor], [self.syncAgainButton.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
[self.syncAgainButton.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor], [self.syncAgainButton.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
[self.syncAgainButton.bottomAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.bottomAnchor], [self.syncAgainButton.bottomAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.bottomAnchor],
[self.syncAgainButton.heightAnchor constraintEqualToConstant:30], [self.syncAgainButton.heightAnchor constraintEqualToConstant:45],
]]; ]];
} }
...@@ -618,4 +623,23 @@ typedef NS_ENUM(NSInteger, HistorySyncStatus) { ...@@ -618,4 +623,23 @@ typedef NS_ENUM(NSInteger, HistorySyncStatus) {
hasFailedItems ? [UIColor systemBlueColor] : [UIColor lightGrayColor]; hasFailedItems ? [UIColor systemBlueColor] : [UIColor lightGrayColor];
} }
- (NSDictionary *)normalizedRow:(id)row {
if ([row isKindOfClass:[NSDictionary class]]) {
return row;
}
if ([row isKindOfClass:[NSString class]]) {
NSData *data = [(NSString *)row dataUsingEncoding:NSUTF8StringEncoding];
if (!data) return nil;
id json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
if ([json isKindOfClass:[NSDictionary class]]) {
return json;
}
}
return nil;
}
@end @end
...@@ -52,6 +52,7 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE"; ...@@ -52,6 +52,7 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE";
@property (nonatomic, strong) UIButton *downloadUnitsToggle; @property (nonatomic, strong) UIButton *downloadUnitsToggle;
@property (nonatomic, strong) SyncProgressOverlay *progressOverlay; @property (nonatomic, strong) SyncProgressOverlay *progressOverlay;
@property (nonatomic, strong) UIButton *yesBtn;
@end @end
...@@ -329,8 +330,8 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE"; ...@@ -329,8 +330,8 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE";
- (void)showSyncConfirmationModal { - (void)showSyncConfirmationModal {
if (self.syncModalOverlay) return; // already showing if (self.syncModalOverlay) return; // already showing
self.shouldSubmitPending = YES; self.shouldSubmitPending = NO;
self.shouldDownloadUnits = YES; self.shouldDownloadUnits = NO;
[self buildSyncModal]; [self buildSyncModal];
} }
...@@ -377,17 +378,20 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE"; ...@@ -377,17 +378,20 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE";
// --- Toggles --- // --- Toggles ---
self.submitPendingToggle = self.submitPendingToggle =
[self createToggleWithTitle:@"Submit all pending" [self createToggleWithTitle:@"Submit all pending"
selected:YES selected:NO
action:@selector(onSubmitPendingToggle)]; action:@selector(onSubmitPendingToggle)];
self.downloadUnitsToggle = self.downloadUnitsToggle =
[self createToggleWithTitle:@"Download all selected units" [self createToggleWithTitle:@"Download all selected units"
selected:YES selected:NO
action:@selector(onDownloadUnitsToggle)]; action:@selector(onDownloadUnitsToggle)];
// --- Buttons --- // --- Buttons ---
UIButton *noBtn = [self modalButton:@"NO" action:@selector(onSyncModalNo)]; UIButton *noBtn = [self modalButton:@"NO" action:@selector(onSyncModalNo)];
UIButton *yesBtn = [self modalButton:@"YES" action:@selector(onSyncModalYes)]; self.yesBtn = [self modalButton:@"YES" action:@selector(onSyncModalYes)];
self.yesBtn.enabled = self.shouldSubmitPending || self.shouldDownloadUnits;
self.yesBtn.alpha = self.yesBtn.enabled ? 1.0 : 0.5;
// --- Layout --- // --- Layout ---
UIStackView *stack = [[UIStackView alloc] initWithArrangedSubviews:@[ UIStackView *stack = [[UIStackView alloc] initWithArrangedSubviews:@[
...@@ -396,7 +400,7 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE"; ...@@ -396,7 +400,7 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE";
self.submitPendingToggle, self.submitPendingToggle,
self.downloadUnitsToggle, self.downloadUnitsToggle,
noBtn, noBtn,
yesBtn self.yesBtn
]]; ]];
stack.axis = UILayoutConstraintAxisVertical; stack.axis = UILayoutConstraintAxisVertical;
...@@ -487,12 +491,14 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE"; ...@@ -487,12 +491,14 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE";
self.shouldSubmitPending = !self.shouldSubmitPending; self.shouldSubmitPending = !self.shouldSubmitPending;
self.submitPendingToggle.selected = self.shouldSubmitPending; self.submitPendingToggle.selected = self.shouldSubmitPending;
[self updateToggleAppearance:self.submitPendingToggle title:@"Submit all pending"]; [self updateToggleAppearance:self.submitPendingToggle title:@"Submit all pending"];
[self updateYesButtonState];
} }
- (void)onDownloadUnitsToggle { - (void)onDownloadUnitsToggle {
self.shouldDownloadUnits = !self.shouldDownloadUnits; self.shouldDownloadUnits = !self.shouldDownloadUnits;
self.downloadUnitsToggle.selected = self.shouldDownloadUnits; self.downloadUnitsToggle.selected = self.shouldDownloadUnits;
[self updateToggleAppearance:self.downloadUnitsToggle title:@"Download all selected units"]; [self updateToggleAppearance:self.downloadUnitsToggle title:@"Download all selected units"];
[self updateYesButtonState];
} }
- (void)updateToggleAppearance:(UIButton *)btn title:(NSString *)title { - (void)updateToggleAppearance:(UIButton *)btn title:(NSString *)title {
...@@ -530,15 +536,20 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE"; ...@@ -530,15 +536,20 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE";
[[OfflineSyncManager shared] [[OfflineSyncManager shared]
processNextItemWithProgress:^(NSInteger step, NSInteger total, NSString *title) { processNextItemWithProgress:^(NSInteger step, NSInteger total, NSString *title) {
NSLog(@"📊 progress %ld/%ld %@", (long)step, (long)total, title);
[self.progressOverlay updateStep:step [self.progressOverlay updateStep:step
total:total total:total
title:title]; title:title];
} completion:^(BOOL finished) { } completion:^(BOOL finished) {
NSLog(@"🎉 Offline sync COMPLETED finished=%d", finished);
self.pendingItemCount = [self loadPendingItemCount]; self.pendingItemCount = [self loadPendingItemCount];
self.historyCount = [self loadHistoryItemCount]; self.historyCount = [self loadHistoryItemCount];
if (!self.shouldDownloadUnits) {
[self finishSyncWithSuccess:finished];
}
[self refreshUI]; [self refreshUI];
}]; }];
} }
...@@ -620,4 +631,11 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE"; ...@@ -620,4 +631,11 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE";
return [formatter stringFromDate:date]; return [formatter stringFromDate:date];
} }
- (void)updateYesButtonState {
BOOL enabled = self.shouldSubmitPending || self.shouldDownloadUnits;
self.yesBtn.enabled = enabled;
self.yesBtn.alpha = enabled ? 1.0 : 0.5;
}
@end @end
// APIClient.mm // APIClient.mm
#import "APIClient.h" #import "APIClient.h"
#import "APIConfig.h"
@implementation APIClient @implementation APIClient
+ (void)requestAccessItem:(NSString *)unitId + (void)requestAccessItem:(NSString *)unitId
completion:(void(^)(BOOL success, NSDictionary * _Nullable response, NSError * _Nullable error))completion { completion:(void(^)(BOOL success, NSDictionary * _Nullable response, NSError * _Nullable error))completion {
NSString *urlString = @"https://kitadev.commudesk.com/api/owner/plan/getAccessItem?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6ImF0aGlyYWh6YWlkaUBjb252ZXAuY29tIiwicGFzc3dvcmQiOiIkMnkkMTAkNU9xYklqMnZ6UHJwckJrcVd5c3I2LmJCc1hVYS9Hd014eUhnL3RhUUhPUkNQcGdmTG8yakciLCJzdWIiOjIxNzAsImlzcyI6Imh0dHBzOi8va2l0YWRldi5jb21tdWRlc2suY29tL2FwaS9vd25lci9hdXRoL3Bhc3N3b3JkbGVzc19sb2dpbiIsImlhdCI6MTc2MDMxNTM1MiwiZXhwIjoyMDc1ODg0ODcyLCJuYmYiOjE3NjAzMTUzNTIsImp0aSI6IktoQmNyQnJEdDVNdDZlWmMifQ.JnxGbZ7hTeoOr6oibIzZMphgyKtTo2Aq9Efx6mrGfFA"; NSURL *url = [APIConfig urlWithPath:@"/owner/plan/getAccessItem"];
NSURL *url = [NSURL URLWithString:urlString];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST"; request.HTTPMethod = @"POST";
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
NSDictionary *deviceInfo = @{
@"OS": @"AND",
@"IMEI": @"AND:2a2f13ff17b1cbb5",
@"OS_VERSION": @"35",
@"MODEL": @"2306EPN60G",
@"APP_VERSION": @"1.22.26"
};
NSDictionary *jsonBody = @{ NSDictionary *jsonBody = @{
@"data": @{ @"data": @{
@"os": @"AND", @"os": @"AND",
@"drawing_plan_id": unitId ?: @"", @"drawing_plan_id": unitId ?: @"",
@"information": deviceInfo, @"information": [APIConfig deviceInfo],
} }
}; };
NSLog(@"🌍 SUBMIT URL: %@", urlString); NSLog(@"🌍 SUBMIT URL: %@", url.absoluteString);
NSLog(@"📦 SUBMIT Body JSON: %@", jsonBody); NSLog(@"📦 SUBMIT Body JSON: %@", jsonBody);
NSError *jsonError; NSError *jsonError;
...@@ -82,8 +73,7 @@ ...@@ -82,8 +73,7 @@
+ (void)requestGetUnitPlan:(void (^)(NSDictionary *data, NSError *error))completion { + (void)requestGetUnitPlan:(void (^)(NSDictionary *data, NSError *error))completion {
// ✅ Construct URL (with token) // ✅ Construct URL (with token)
NSString *urlString = @"https://kitadev.commudesk.com/api/owner/plan/getLocationByUnit?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6ImF0aGlyYWh6YWlkaUBjb252ZXAuY29tIiwicGFzc3dvcmQiOiIkMnkkMTAkNU9xYklqMnZ6UHJwckJrcVd5c3I2LmJCc1hVYS9Hd014eUhnL3RhUUhPUkNQcGdmTG8yakciLCJzdWIiOjIxNzAsImlzcyI6Imh0dHBzOi8va2l0YWRldi5jb21tdWRlc2suY29tL2FwaS9vd25lci9hdXRoL3Bhc3N3b3JkbGVzc19sb2dpbiIsImlhdCI6MTc2MDMxNTM1MiwiZXhwIjoyMDc1ODg0ODcyLCJuYmYiOjE3NjAzMTUzNTIsImp0aSI6IktoQmNyQnJEdDVNdDZlWmMifQ.JnxGbZ7hTeoOr6oibIzZMphgyKtTo2Aq9Efx6mrGfFA"; NSURL *url = [APIConfig urlWithPath:@"/owner/plan/getLocationByUnit"];
NSURL *url = [NSURL URLWithString:urlString];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST"; request.HTTPMethod = @"POST";
...@@ -111,7 +101,7 @@ ...@@ -111,7 +101,7 @@
appendFormField(@"data[drawing_plan_id]",@"135"); appendFormField(@"data[drawing_plan_id]",@"135");
// You can reuse your existing getDeviceInfo() method to produce this JSON string // You can reuse your existing getDeviceInfo() method to produce this JSON string
NSString *deviceInfo = @"{\"OS\":\"AND\",\"IMEI\":\"AND:2a2f13ff17b1cbb5\",\"OS_VERSION\":\"35\",\"MODEL\":\"2306EPN60G\",\"APP_VERSION\":\"1.22.26\"}"; NSString *deviceInfo = [APIConfig deviceInfoJSONString];
appendFormField(@"data[information]", deviceInfo); appendFormField(@"data[information]", deviceInfo);
// 🔚 End boundary // 🔚 End boundary
...@@ -120,7 +110,7 @@ ...@@ -120,7 +110,7 @@
request.HTTPBody = body; request.HTTPBody = body;
// 🌍 Debug logs // 🌍 Debug logs
NSLog(@"🌍 [requestUnitPlan] URL: %@", urlString); NSLog(@"🌍 [requestUnitPlan] URL: %@", url.absoluteString);
NSLog(@"📦 [requestUnitPlan] Body:\n%@", [[NSString alloc] initWithData:body encoding:NSUTF8StringEncoding]); NSLog(@"📦 [requestUnitPlan] Body:\n%@", [[NSString alloc] initWithData:body encoding:NSUTF8StringEncoding]);
// 🌐 Perform request // 🌐 Perform request
...@@ -168,8 +158,8 @@ ...@@ -168,8 +158,8 @@
+ (void)requestSettingsByLocation:(NSString *)locationID + (void)requestSettingsByLocation:(NSString *)locationID
projectID:(NSString *)projectID projectID:(NSString *)projectID
completion:(void (^)(NSDictionary *data, NSError *error))completion { completion:(void (^)(NSDictionary *data, NSError *error))completion {
NSString *urlString = @"https://kitadev.commudesk.com/api/issue/getGeneralSettingByLocation?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6ImF0aGlyYWh6YWlkaUBjb252ZXAuY29tIiwicGFzc3dvcmQiOiIkMnkkMTAkNU9xYklqMnZ6UHJwckJrcVd5c3I2LmJCc1hVYS9Hd014eUhnL3RhUUhPUkNQcGdmTG8yakciLCJzdWIiOjIxNzAsImlzcyI6Imh0dHBzOi8va2l0YWRldi5jb21tdWRlc2suY29tL2FwaS9vd25lci9hdXRoL3Bhc3N3b3JkbGVzc19sb2dpbiIsImlhdCI6MTc2MDMxNTM1MiwiZXhwIjoyMDc1ODg0ODcyLCJuYmYiOjE3NjAzMTUzNTIsImp0aSI6IktoQmNyQnJEdDVNdDZlWmMifQ.JnxGbZ7hTeoOr6oibIzZMphgyKtTo2Aq9Efx6mrGfFA";
NSURL *url = [NSURL URLWithString:urlString]; NSURL *url = [APIConfig urlWithPath:@"/owner/plan/getGeneralSettingByLocation"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST"; request.HTTPMethod = @"POST";
...@@ -197,7 +187,7 @@ ...@@ -197,7 +187,7 @@
appendFormField(@"data[project_id]", projectID ?: @"0"); appendFormField(@"data[project_id]", projectID ?: @"0");
appendFormField(@"data[location_id]", locationID ?: @"0"); appendFormField(@"data[location_id]", locationID ?: @"0");
NSString *deviceInfo = @"{\"OS\":\"AND\",\"IMEI\":\"AND:2a2f13ff17b1cbb5\",\"OS_VERSION\":\"35\",\"MODEL\":\"2306EPN60G\",\"APP_VERSION\":\"1.22.26\"}"; NSString *deviceInfo = [APIConfig deviceInfoJSONString];
appendFormField(@"data[information]", deviceInfo); appendFormField(@"data[information]", deviceInfo);
// End boundary // End boundary
...@@ -206,7 +196,7 @@ ...@@ -206,7 +196,7 @@
request.HTTPBody = body; request.HTTPBody = body;
// 🌍 Debug logs // 🌍 Debug logs
NSLog(@"🌍 [requestSettingsByLocation] URL: %@", urlString); NSLog(@"🌍 [requestSettingsByLocation] URL: %@", url.absoluteString);
NSLog(@"📦 [requestSettingsByLocation] Body:\n%@", [[NSString alloc] initWithData:body encoding:NSUTF8StringEncoding]); NSLog(@"📦 [requestSettingsByLocation] Body:\n%@", [[NSString alloc] initWithData:body encoding:NSUTF8StringEncoding]);
// 🌐 Send request // 🌐 Send request
...@@ -257,7 +247,7 @@ ...@@ -257,7 +247,7 @@
images:(NSArray<UIImage *> * _Nullable)images images:(NSArray<UIImage *> * _Nullable)images
completion:(void (^)(NSDictionary * _Nullable response, NSError * _Nullable error))completion { completion:(void (^)(NSDictionary * _Nullable response, NSError * _Nullable error))completion {
NSString *urlString = @"https://kitadev.commudesk.com/api/owner/issue/addIssue?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6ImF0aGlyYWh6YWlkaUBjb252ZXAuY29tIiwicGFzc3dvcmQiOiIkMnkkMTAkNU9xYklqMnZ6UHJwckJrcVd5c3I2LmJCc1hVYS9Hd014eUhnL3RhUUhPUkNQcGdmTG8yakciLCJzdWIiOjIxNzAsImlzcyI6Imh0dHBzOi8va2l0YWRldi5jb21tdWRlc2suY29tL2FwaS9vd25lci9hdXRoL3Bhc3N3b3JkbGVzc19sb2dpbiIsImlhdCI6MTc2MDMxNTM1MiwiZXhwIjoyMDc1ODg0ODcyLCJuYmYiOjE3NjAzMTUzNTIsImp0aSI6IktoQmNyQnJEdDVNdDZlWmMifQ.JnxGbZ7hTeoOr6oibIzZMphgyKtTo2Aq9Efx6mrGfFA"; NSURL *url = [NSURL URLWithString:urlString]; NSURL *url = [APIConfig urlWithPath:@"/owner/issue/addIssue"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST"; request.HTTPMethod = @"POST";
...@@ -349,7 +339,7 @@ ...@@ -349,7 +339,7 @@
images:(NSArray<UIImage *> * _Nullable)images images:(NSArray<UIImage *> * _Nullable)images
completion:(void (^)(NSDictionary * _Nullable response, NSError * _Nullable error))completion { completion:(void (^)(NSDictionary * _Nullable response, NSError * _Nullable error))completion {
NSString *urlString = @"https://kitadev.commudesk.com/api/owner/issue/updateIssue?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6ImF0aGlyYWh6YWlkaUBjb252ZXAuY29tIiwicGFzc3dvcmQiOiIkMnkkMTAkNU9xYklqMnZ6UHJwckJrcVd5c3I2LmJCc1hVYS9Hd014eUhnL3RhUUhPUkNQcGdmTG8yakciLCJzdWIiOjIxNzAsImlzcyI6Imh0dHBzOi8va2l0YWRldi5jb21tdWRlc2suY29tL2FwaS9vd25lci9hdXRoL3Bhc3N3b3JkbGVzc19sb2dpbiIsImlhdCI6MTc2MDMxNTM1MiwiZXhwIjoyMDc1ODg0ODcyLCJuYmYiOjE3NjAzMTUzNTIsImp0aSI6IktoQmNyQnJEdDVNdDZlWmMifQ.JnxGbZ7hTeoOr6oibIzZMphgyKtTo2Aq9Efx6mrGfFA"; NSURL *url = [NSURL URLWithString:urlString]; NSURL *url = [APIConfig urlWithPath:@"/owner/issue/updateIssue"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST"; request.HTTPMethod = @"POST";
...@@ -453,8 +443,7 @@ ...@@ -453,8 +443,7 @@
+ (void)requestAppointment:(void (^)(NSDictionary *data, NSError *error))completion { + (void)requestAppointment:(void (^)(NSDictionary *data, NSError *error))completion {
// ✅ URL // ✅ URL
NSString *urlString = @"https://kitadev.commudesk.com/api/owner/appointment/getSlot?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6ImF0aGlyYWh6YWlkaUBjb252ZXAuY29tIiwicGFzc3dvcmQiOiIkMnkkMTAkNU9xYklqMnZ6UHJwckJrcVd5c3I2LmJCc1hVYS9Hd014eUhnL3RhUUhPUkNQcGdmTG8yakciLCJzdWIiOjIxNzAsImlzcyI6Imh0dHBzOi8va2l0YWRldi5jb21tdWRlc2suY29tL2FwaS9vd25lci9hdXRoL3Bhc3N3b3JkbGVzc19sb2dpbiIsImlhdCI6MTc2MDMxNTM1MiwiZXhwIjoyMDc1ODg0ODcyLCJuYmYiOjE3NjAzMTUzNTIsImp0aSI6IktoQmNyQnJEdDVNdDZlWmMifQ.JnxGbZ7hTeoOr6oibIzZMphgyKtTo2Aq9Efx6mrGfFA"; NSURL *url = [APIConfig urlWithPath:@"/owner/appointment/getSlot"];
NSURL *url = [NSURL URLWithString:urlString];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST"; request.HTTPMethod = @"POST";
...@@ -483,7 +472,7 @@ ...@@ -483,7 +472,7 @@
request.HTTPBody = bodyData; request.HTTPBody = bodyData;
// 🧭 Debug logs // 🧭 Debug logs
NSLog(@"🌍 URL: %@", urlString); NSLog(@"🌍 URL: %@", url.absoluteString);
NSLog(@"📦 Body JSON: %@", jsonBody); NSLog(@"📦 Body JSON: %@", jsonBody);
// ✅ Make request // ✅ Make request
...@@ -518,8 +507,7 @@ ...@@ -518,8 +507,7 @@
+ (void)requestGeneralInfo:(void (^)(NSDictionary *data, NSError *error))completion { + (void)requestGeneralInfo:(void (^)(NSDictionary *data, NSError *error))completion {
// ✅ URL // ✅ URL
NSString *urlString = @"https://kitadev.commudesk.com/api/owner/plan/getUnitInfo?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6ImF0aGlyYWh6YWlkaUBjb252ZXAuY29tIiwicGFzc3dvcmQiOiIkMnkkMTAkNU9xYklqMnZ6UHJwckJrcVd5c3I2LmJCc1hVYS9Hd014eUhnL3RhUUhPUkNQcGdmTG8yakciLCJzdWIiOjIxNzAsImlzcyI6Imh0dHBzOi8va2l0YWRldi5jb21tdWRlc2suY29tL2FwaS9vd25lci9hdXRoL3Bhc3N3b3JkbGVzc19sb2dpbiIsImlhdCI6MTc2MDMxNTM1MiwiZXhwIjoyMDc1ODg0ODcyLCJuYmYiOjE3NjAzMTUzNTIsImp0aSI6IktoQmNyQnJEdDVNdDZlWmMifQ.JnxGbZ7hTeoOr6oibIzZMphgyKtTo2Aq9Efx6mrGfFA"; NSURL *url = [APIConfig urlWithPath:@"/owner/plan/getUnitInfo"];
NSURL *url = [NSURL URLWithString:urlString];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST"; request.HTTPMethod = @"POST";
...@@ -547,7 +535,7 @@ ...@@ -547,7 +535,7 @@
request.HTTPBody = bodyData; request.HTTPBody = bodyData;
// 🧭 Debug logs // 🧭 Debug logs
NSLog(@"🌍 URL: %@", urlString); NSLog(@"🌍 URL: %@", url.absoluteString);
NSLog(@"📦 Body JSON: %@", jsonBody); NSLog(@"📦 Body JSON: %@", jsonBody);
// ✅ Make request // ✅ Make request
...@@ -583,10 +571,9 @@ ...@@ -583,10 +571,9 @@
+ (void)submitAppointment:(NSDictionary *)appointmentData + (void)submitAppointment:(NSDictionary *)appointmentData
completion:(void(^)(BOOL success, NSDictionary * _Nullable response, NSError * _Nullable error))completion { completion:(void(^)(BOOL success, NSDictionary * _Nullable response, NSError * _Nullable error))completion {
NSString *urlString = @"https://kitadev.commudesk.com/api/owner/appointment/makeAppointment?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6ImF0aGlyYWh6YWlkaUBjb252ZXAuY29tIiwicGFzc3dvcmQiOiIkMnkkMTAkNU9xYklqMnZ6UHJwckJrcVd5c3I2LmJCc1hVYS9Hd014eUhnL3RhUUhPUkNQcGdmTG8yakciLCJzdWIiOjIxNzAsImlzcyI6Imh0dHBzOi8va2l0YWRldi5jb21tdWRlc2suY29tL2FwaS9vd25lci9hdXRoL3Bhc3N3b3JkbGVzc19sb2dpbiIsImlhdCI6MTc2MDMxNTM1MiwiZXhwIjoyMDc1ODg0ODcyLCJuYmYiOjE3NjAzMTUzNTIsImp0aSI6IktoQmNyQnJEdDVNdDZlWmMifQ.JnxGbZ7hTeoOr6oibIzZMphgyKtTo2Aq9Efx6mrGfFA"; NSURL *url = [APIConfig urlWithPath:@"/owner/appointment/makeAppointment"];
NSURL *url = [NSURL URLWithString:urlString];
NSLog(@"🌍 SUBMIT (NO REP) URL: %@", urlString); NSLog(@"🌍 SUBMIT (NO REP) URL: %@", url.absoluteString);
NSLog(@"📦 SUBMIT (NO REP) Body JSON: %@", appointmentData); NSLog(@"📦 SUBMIT (NO REP) Body JSON: %@", appointmentData);
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
...@@ -643,10 +630,8 @@ ...@@ -643,10 +630,8 @@
+ (void)submitRepresentativeAppointment:(NSDictionary *)repData + (void)submitRepresentativeAppointment:(NSDictionary *)repData
completion:(void(^)(BOOL success, NSDictionary * _Nullable response, NSError * _Nullable error))completion { completion:(void(^)(BOOL success, NSDictionary * _Nullable response, NSError * _Nullable error))completion {
NSURL *url = [APIConfig urlWithPath:@"/owner/appointment/makeAppointment"];
NSString *urlString = @"https://kitadev.commudesk.com/api/owner/appointment/makeAppointment?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6ImF0aGlyYWh6YWlkaUBjb252ZXAuY29tIiwicGFzc3dvcmQiOiIkMnkkMTAkNU9xYklqMnZ6UHJwckJrcVd5c3I2LmJCc1hVYS9Hd014eUhnL3RhUUhPUkNQcGdmTG8yakciLCJzdWIiOjIxNzAsImlzcyI6Imh0dHBzOi8va2l0YWRldi5jb21tdWRlc2suY29tL2FwaS9vd25lci9hdXRoL3Bhc3N3b3JkbGVzc19sb2dpbiIsImlhdCI6MTc2MDMxNTM1MiwiZXhwIjoyMDc1ODg0ODcyLCJuYmYiOjE3NjAzMTUzNTIsImp0aSI6IktoQmNyQnJEdDVNdDZlWmMifQ.JnxGbZ7hTeoOr6oibIzZMphgyKtTo2Aq9Efx6mrGfFA"; NSLog(@"🌍 SUBMIT URL: %@", url.absoluteString);
NSURL *url = [NSURL URLWithString:urlString];
NSLog(@"🌍 SUBMIT URL: %@", urlString);
// Create request // Create request
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
...@@ -711,8 +696,7 @@ ...@@ -711,8 +696,7 @@
reason:(NSString *)reason reason:(NSString *)reason
completion:(void (^)(NSDictionary *data, NSError *error))completion { completion:(void (^)(NSDictionary *data, NSError *error))completion {
// ✅ URL // ✅ URL
NSString *urlString = @"https://kitadev.commudesk.com/api/owner/appointment/cancelAppointment?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6ImF0aGlyYWh6YWlkaUBjb252ZXAuY29tIiwicGFzc3dvcmQiOiIkMnkkMTAkNU9xYklqMnZ6UHJwckJrcVd5c3I2LmJCc1hVYS9Hd014eUhnL3RhUUhPUkNQcGdmTG8yakciLCJzdWIiOjIxNzAsImlzcyI6Imh0dHBzOi8va2l0YWRldi5jb21tdWRlc2suY29tL2FwaS9vd25lci9hdXRoL3Bhc3N3b3JkbGVzc19sb2dpbiIsImlhdCI6MTc2MDMxNTM1MiwiZXhwIjoyMDc1ODg0ODcyLCJuYmYiOjE3NjAzMTUzNTIsImp0aSI6IktoQmNyQnJEdDVNdDZlWmMifQ.JnxGbZ7hTeoOr6oibIzZMphgyKtTo2Aq9Efx6mrGfFA"; NSURL *url = [APIConfig urlWithPath:@"/owner/appointment/cancelAppointment"];
NSURL *url = [NSURL URLWithString:urlString];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST"; request.HTTPMethod = @"POST";
...@@ -741,7 +725,7 @@ ...@@ -741,7 +725,7 @@
request.HTTPBody = bodyData; request.HTTPBody = bodyData;
// 🧭 Debug logs // 🧭 Debug logs
NSLog(@"🌍 URL: %@", urlString); NSLog(@"🌍 URL: %@", url.absoluteString);
NSLog(@"📦 Body JSON: %@", jsonBody); NSLog(@"📦 Body JSON: %@", jsonBody);
// ✅ Make request // ✅ Make request
...@@ -775,16 +759,11 @@ ...@@ -775,16 +759,11 @@
} }
+ (void)requestDashboardInfo:(void (^)(NSDictionary *data, NSError *error))completion { + (void)requestDashboardInfo:(void (^)(NSDictionary *data, NSError *error))completion {
NSString *urlString = @"https://kitadev.commudesk.com/api/owner/plan/getIssueUpdateAppointmentInfo"; NSURL *url = [APIConfig urlWithPath:@"/owner/plan/getIssueUpdateAppointmentInfo"];
NSURL *url = [NSURL URLWithString:urlString];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST"; request.HTTPMethod = @"POST";
// 🪪 Authorization header
NSString *token = @"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6ImF0aGlyYWh6YWlkaUBjb252ZXAuY29tIiwicGFzc3dvcmQiOiIkMnkkMTAkNU9xYklqMnZ6UHJwckJrcVd5c3I2LmJCc1hVYS9Hd014eUhnL3RhUUhPUkNQcGdmTG8yakciLCJzdWIiOjIxNzAsImlzcyI6Imh0dHBzOi8va2l0YWRldi5jb21tdWRlc2suY29tL2FwaS9vd25lci9hdXRoL3Bhc3N3b3JkbGVzc19sb2dpbiIsImlhdCI6MTc2MDMxNTM1MiwiZXhwIjoyMDc1ODg0ODcyLCJuYmYiOjE3NjAzMTUzNTIsImp0aSI6IktoQmNyQnJEdDVNdDZlWmMifQ.JnxGbZ7hTeoOr6oibIzZMphgyKtTo2Aq9Efx6mrGfFA";
[request setValue:[NSString stringWithFormat:@"Bearer %@", token] forHTTPHeaderField:@"Authorization"];
// 🧱 Create multipart form-data body // 🧱 Create multipart form-data body
NSString *boundary = [NSString stringWithFormat:@"Boundary-%@", [[NSUUID UUID] UUIDString]]; NSString *boundary = [NSString stringWithFormat:@"Boundary-%@", [[NSUUID UUID] UUIDString]];
NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary]; NSString *contentType = [NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary];
...@@ -811,8 +790,7 @@ ...@@ -811,8 +790,7 @@
request.HTTPBody = body; request.HTTPBody = body;
// 🌍 Debug logs // 🌍 Debug logs
NSLog(@"🌍 URL: %@", urlString); NSLog(@"🌍 URL: %@", url.absoluteString);
NSLog(@"🪪 Authorization: Bearer %@", token);
NSLog(@"📦 Body:\n%@", [[NSString alloc] initWithData:body encoding:NSUTF8StringEncoding]); NSLog(@"📦 Body:\n%@", [[NSString alloc] initWithData:body encoding:NSUTF8StringEncoding]);
NSURLSessionDataTask *task = [[NSURLSession sharedSession] NSURLSessionDataTask *task = [[NSURLSession sharedSession]
...@@ -839,8 +817,7 @@ ...@@ -839,8 +817,7 @@
+ (void)requestAnnouncement:(void (^)(NSDictionary *data, NSError *error))completion { + (void)requestAnnouncement:(void (^)(NSDictionary *data, NSError *error))completion {
// ✅ Keep token in URL // ✅ Keep token in URL
NSString *urlString = @"https://kitadev.commudesk.com/api/clientAnnouncement?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6ImF0aGlyYWh6YWlkaUBjb252ZXAuY29tIiwicGFzc3dvcmQiOiIkMnkkMTAkNU9xYklqMnZ6UHJwckJrcVd5c3I2LmJCc1hVYS9Hd014eUhnL3RhUUhPUkNQcGdmTG8yakciLCJzdWIiOjIxNzAsImlzcyI6Imh0dHBzOi8va2l0YWRldi5jb21tdWRlc2suY29tL2FwaS9vd25lci9hdXRoL3Bhc3N3b3JkbGVzc19sb2dpbiIsImlhdCI6MTc2MDMxNTM1MiwiZXhwIjoyMDc1ODg0ODcyLCJuYmYiOjE3NjAzMTUzNTIsImp0aSI6IktoQmNyQnJEdDVNdDZlWmMifQ.JnxGbZ7hTeoOr6oibIzZMphgyKtTo2Aq9Efx6mrGfFA"; NSURL *url = [APIConfig urlWithPath:@"/clientAnnouncement"];
NSURL *url = [NSURL URLWithString:urlString];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST"; request.HTTPMethod = @"POST";
...@@ -870,7 +847,7 @@ ...@@ -870,7 +847,7 @@
request.HTTPBody = body; request.HTTPBody = body;
// 🌍 Debug logs // 🌍 Debug logs
NSLog(@"🌍 URL: %@", urlString); NSLog(@"🌍 URL: %@", url.absoluteString);
NSLog(@"📦 Body:\n%@", [[NSString alloc] initWithData:body encoding:NSUTF8StringEncoding]); NSLog(@"📦 Body:\n%@", [[NSString alloc] initWithData:body encoding:NSUTF8StringEncoding]);
NSURLSessionDataTask *task = [[NSURLSession sharedSession] NSURLSessionDataTask *task = [[NSURLSession sharedSession]
...@@ -909,8 +886,7 @@ ...@@ -909,8 +886,7 @@
+ (void)requestHistory:(NSString *)issueID + (void)requestHistory:(NSString *)issueID
completion:(void (^)(NSDictionary *data, NSError *error))completion { completion:(void (^)(NSDictionary *data, NSError *error))completion {
// ✅ URL // ✅ URL
NSString *urlString = @"https://kitadev.commudesk.com/api/owner/issue/getIssueHistory?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6ImF0aGlyYWh6YWlkaUBjb252ZXAuY29tIiwicGFzc3dvcmQiOiIkMnkkMTAkNU9xYklqMnZ6UHJwckJrcVd5c3I2LmJCc1hVYS9Hd014eUhnL3RhUUhPUkNQcGdmTG8yakciLCJzdWIiOjIxNzAsImlzcyI6Imh0dHBzOi8va2l0YWRldi5jb21tdWRlc2suY29tL2FwaS9vd25lci9hdXRoL3Bhc3N3b3JkbGVzc19sb2dpbiIsImlhdCI6MTc2MDMxNTM1MiwiZXhwIjoyMDc1ODg0ODcyLCJuYmYiOjE3NjAzMTUzNTIsImp0aSI6IktoQmNyQnJEdDVNdDZlWmMifQ.JnxGbZ7hTeoOr6oibIzZMphgyKtTo2Aq9Efx6mrGfFA"; NSURL *url = [APIConfig urlWithPath:@"/owner/issue/getIssueHistory"];
NSURL *url = [NSURL URLWithString:urlString];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST"; request.HTTPMethod = @"POST";
...@@ -919,20 +895,11 @@ ...@@ -919,20 +895,11 @@
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
[request setValue:@"application/json" forHTTPHeaderField:@"Accept"]; [request setValue:@"application/json" forHTTPHeaderField:@"Accept"];
// ✅ Build JSON body
NSDictionary *deviceInfo = @{
@"OS": @"AND",
@"IMEI": @"AND:2a2f13ff17b1cbb5",
@"OS_VERSION": @"35",
@"MODEL": @"2306EPN60G",
@"APP_VERSION": @"1.22.26"
};
NSDictionary *jsonBody = @{ NSDictionary *jsonBody = @{
@"data": @{ @"data": @{
@"os": @"AND", @"os": @"AND",
@"issue_id": issueID, @"issue_id": issueID,
@"information": deviceInfo @"information": [APIConfig deviceInfo],
} }
}; };
...@@ -947,7 +914,7 @@ ...@@ -947,7 +914,7 @@
request.HTTPBody = bodyData; request.HTTPBody = bodyData;
// 🧭 Debug logs // 🧭 Debug logs
NSLog(@"🌍 URL: %@", urlString); NSLog(@"🌍 URL: %@", url.absoluteString);
NSLog(@"📦 Body JSON: %@", jsonBody); NSLog(@"📦 Body JSON: %@", jsonBody);
// ✅ Make request // ✅ Make request
...@@ -984,8 +951,7 @@ ...@@ -984,8 +951,7 @@
remarks:(NSString *)remarks remarks:(NSString *)remarks
completion:(void (^)(NSDictionary *data, NSError *error))completion { completion:(void (^)(NSDictionary *data, NSError *error))completion {
// ✅ URL // ✅ URL
NSString *urlString = @"https://kitadev.commudesk.com/api/owner/issue/addInfo?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6ImF0aGlyYWh6YWlkaUBjb252ZXAuY29tIiwicGFzc3dvcmQiOiIkMnkkMTAkNU9xYklqMnZ6UHJwckJrcVd5c3I2LmJCc1hVYS9Hd014eUhnL3RhUUhPUkNQcGdmTG8yakciLCJzdWIiOjIxNzAsImlzcyI6Imh0dHBzOi8va2l0YWRldi5jb21tdWRlc2suY29tL2FwaS9vd25lci9hdXRoL3Bhc3N3b3JkbGVzc19sb2dpbiIsImlhdCI6MTc2MDMxNTM1MiwiZXhwIjoyMDc1ODg0ODcyLCJuYmYiOjE3NjAzMTUzNTIsImp0aSI6IktoQmNyQnJEdDVNdDZlWmMifQ.JnxGbZ7hTeoOr6oibIzZMphgyKtTo2Aq9Efx6mrGfFA"; NSURL *url = [APIConfig urlWithPath:@"/owner/issue/addInfo"];
NSURL *url = [NSURL URLWithString:urlString];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST"; request.HTTPMethod = @"POST";
...@@ -994,22 +960,13 @@ ...@@ -994,22 +960,13 @@
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
[request setValue:@"application/json" forHTTPHeaderField:@"Accept"]; [request setValue:@"application/json" forHTTPHeaderField:@"Accept"];
// ✅ Build JSON body
NSDictionary *deviceInfo = @{
@"OS": @"AND",
@"IMEI": @"AND:2a2f13ff17b1cbb5",
@"OS_VERSION": @"35",
@"MODEL": @"2306EPN60G",
@"APP_VERSION": @"1.22.26"
};
NSDictionary *jsonBody = @{ NSDictionary *jsonBody = @{
@"data": @{ @"data": @{
@"issue_id": issueID, @"issue_id": issueID,
@"remarks": remarks, @"remarks": remarks,
@"os": @"AND", @"os": @"AND",
@"full_response": @"false", @"full_response": @"false",
@"information": deviceInfo @"information": [APIConfig deviceInfo],
} }
}; };
...@@ -1024,7 +981,7 @@ ...@@ -1024,7 +981,7 @@
request.HTTPBody = bodyData; request.HTTPBody = bodyData;
// 🧭 Debug logs // 🧭 Debug logs
NSLog(@"🌍 URL: %@", urlString); NSLog(@"🌍 URL: %@", url.absoluteString);
NSLog(@"📦 Body JSON: %@", jsonBody); NSLog(@"📦 Body JSON: %@", jsonBody);
// ✅ Make request // ✅ Make request
...@@ -1046,7 +1003,7 @@ ...@@ -1046,7 +1003,7 @@
if (jsonParseError) { if (jsonParseError) {
NSLog(@"⚠️ JSON Parse Error: %@", jsonParseError); NSLog(@"⚠️ JSON Parse Error: %@", jsonParseError);
} else { } else {
NSLog(@"✅ Issues API Response:\n%@", jsonResponse); NSLog(@"✅ Add Info API Response:\n%@", jsonResponse);
} }
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
...@@ -1064,7 +1021,7 @@ ...@@ -1064,7 +1021,7 @@
mediaType:(NSString *)mediaType mediaType:(NSString *)mediaType
completion:(void (^)(NSDictionary *data, NSError *error))completion { completion:(void (^)(NSDictionary *data, NSError *error))completion {
NSString *urlString = @"https://kitadev.commudesk.com/api/owner/issue/addInfo?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6ImF0aGlyYWh6YWlkaUBjb252ZXAuY29tIiwicGFzc3dvcmQiOiIkMnkkMTAkNU9xYklqMnZ6UHJwckJrcVd5c3I2LmJCc1hVYS9Hd014eUhnL3RhUUhPUkNQcGdmTG8yakciLCJzdWIiOjIxNzAsImlzcyI6Imh0dHBzOi8va2l0YWRldi5jb21tdWRlc2suY29tL2FwaS9vd25lci9hdXRoL3Bhc3N3b3JkbGVzc19sb2dpbiIsImlhdCI6MTc2MDMxNTM1MiwiZXhwIjoyMDc1ODg0ODcyLCJuYmYiOjE3NjAzMTUzNTIsImp0aSI6IktoQmNyQnJEdDVNdDZlWmMifQ.JnxGbZ7hTeoOr6oibIzZMphgyKtTo2Aq9Efx6mrGfFA"; NSURL *url = [NSURL URLWithString:urlString]; NSURL *url = [APIConfig urlWithPath:@"/owner/issue/addInfo"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST"; request.HTTPMethod = @"POST";
...@@ -1177,8 +1134,7 @@ ...@@ -1177,8 +1134,7 @@
+ (void)requestIssues:(void (^)(NSDictionary *data, NSError *error))completion { + (void)requestIssues:(void (^)(NSDictionary *data, NSError *error))completion {
// ✅ URL // ✅ URL
NSString *urlString = @"https://kitadev.commudesk.com/api/owner/issue/getIssue?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6ImF0aGlyYWh6YWlkaUBjb252ZXAuY29tIiwicGFzc3dvcmQiOiIkMnkkMTAkNU9xYklqMnZ6UHJwckJrcVd5c3I2LmJCc1hVYS9Hd014eUhnL3RhUUhPUkNQcGdmTG8yakciLCJzdWIiOjIxNzAsImlzcyI6Imh0dHBzOi8va2l0YWRldi5jb21tdWRlc2suY29tL2FwaS9vd25lci9hdXRoL3Bhc3N3b3JkbGVzc19sb2dpbiIsImlhdCI6MTc2MDMxNTM1MiwiZXhwIjoyMDc1ODg0ODcyLCJuYmYiOjE3NjAzMTUzNTIsImp0aSI6IktoQmNyQnJEdDVNdDZlWmMifQ.JnxGbZ7hTeoOr6oibIzZMphgyKtTo2Aq9Efx6mrGfFA"; NSURL *url = [APIConfig urlWithPath:@"/owner/issue/getIssue"];
NSURL *url = [NSURL URLWithString:urlString];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST"; request.HTTPMethod = @"POST";
...@@ -1187,20 +1143,11 @@ ...@@ -1187,20 +1143,11 @@
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
[request setValue:@"application/json" forHTTPHeaderField:@"Accept"]; [request setValue:@"application/json" forHTTPHeaderField:@"Accept"];
// ✅ Build JSON body
NSDictionary *deviceInfo = @{
@"OS": @"AND",
@"IMEI": @"AND:2a2f13ff17b1cbb5",
@"OS_VERSION": @"35",
@"MODEL": @"2306EPN60G",
@"APP_VERSION": @"1.22.26"
};
NSDictionary *jsonBody = @{ NSDictionary *jsonBody = @{
@"data": @{ @"data": @{
@"os": @"AND", @"os": @"AND",
@"drawing_plan_id": @"135", @"drawing_plan_id": @"135",
@"information": deviceInfo @"information": [APIConfig deviceInfo],
} }
}; };
...@@ -1215,7 +1162,7 @@ ...@@ -1215,7 +1162,7 @@
request.HTTPBody = bodyData; request.HTTPBody = bodyData;
// 🧭 Debug logs // 🧭 Debug logs
NSLog(@"🌍 URL: %@", urlString); NSLog(@"🌍 URL: %@", url.absoluteString);
NSLog(@"📦 Body JSON: %@", jsonBody); NSLog(@"📦 Body JSON: %@", jsonBody);
// ✅ Make request // ✅ Make request
...@@ -1252,8 +1199,7 @@ ...@@ -1252,8 +1199,7 @@
remarks:(NSString *)remarks remarks:(NSString *)remarks
completion:(void (^)(NSDictionary *data, NSError *error))completion { completion:(void (^)(NSDictionary *data, NSError *error))completion {
// ✅ URL // ✅ URL
NSString *urlString = @"https://kitadev.commudesk.com/api/owner/issue/voidIssue?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6ImF0aGlyYWh6YWlkaUBjb252ZXAuY29tIiwicGFzc3dvcmQiOiIkMnkkMTAkNU9xYklqMnZ6UHJwckJrcVd5c3I2LmJCc1hVYS9Hd014eUhnL3RhUUhPUkNQcGdmTG8yakciLCJzdWIiOjIxNzAsImlzcyI6Imh0dHBzOi8va2l0YWRldi5jb21tdWRlc2suY29tL2FwaS9vd25lci9hdXRoL3Bhc3N3b3JkbGVzc19sb2dpbiIsImlhdCI6MTc2MDMxNTM1MiwiZXhwIjoyMDc1ODg0ODcyLCJuYmYiOjE3NjAzMTUzNTIsImp0aSI6IktoQmNyQnJEdDVNdDZlWmMifQ.JnxGbZ7hTeoOr6oibIzZMphgyKtTo2Aq9Efx6mrGfFA"; NSURL *url = [APIConfig urlWithPath:@"/owner/issue/voidIssue"];
NSURL *url = [NSURL URLWithString:urlString];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST"; request.HTTPMethod = @"POST";
...@@ -1289,7 +1235,7 @@ ...@@ -1289,7 +1235,7 @@
request.HTTPBody = bodyData; request.HTTPBody = bodyData;
// 🧭 Debug logs // 🧭 Debug logs
NSLog(@"🌍 URL: %@", urlString); NSLog(@"🌍 URL: %@", url.absoluteString);
NSLog(@"📦 Body JSON: %@", jsonBody); NSLog(@"📦 Body JSON: %@", jsonBody);
// ✅ Make request // ✅ Make request
...@@ -1326,8 +1272,7 @@ ...@@ -1326,8 +1272,7 @@
remarks:(NSString *)remarks remarks:(NSString *)remarks
completion:(void (^)(NSDictionary *data, NSError *error))completion { completion:(void (^)(NSDictionary *data, NSError *error))completion {
// ✅ URL // ✅ URL
NSString *urlString = @"https://kitadev.commudesk.com/api/owner/issue/deleteIssue?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6ImF0aGlyYWh6YWlkaUBjb252ZXAuY29tIiwicGFzc3dvcmQiOiIkMnkkMTAkNU9xYklqMnZ6UHJwckJrcVd5c3I2LmJCc1hVYS9Hd014eUhnL3RhUUhPUkNQcGdmTG8yakciLCJzdWIiOjIxNzAsImlzcyI6Imh0dHBzOi8va2l0YWRldi5jb21tdWRlc2suY29tL2FwaS9vd25lci9hdXRoL3Bhc3N3b3JkbGVzc19sb2dpbiIsImlhdCI6MTc2MDMxNTM1MiwiZXhwIjoyMDc1ODg0ODcyLCJuYmYiOjE3NjAzMTUzNTIsImp0aSI6IktoQmNyQnJEdDVNdDZlWmMifQ.JnxGbZ7hTeoOr6oibIzZMphgyKtTo2Aq9Efx6mrGfFA"; NSURL *url = [APIConfig urlWithPath:@"/owner/issue/deleteIssue"];
NSURL *url = [NSURL URLWithString:urlString];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST"; request.HTTPMethod = @"POST";
...@@ -1356,7 +1301,7 @@ ...@@ -1356,7 +1301,7 @@
request.HTTPBody = bodyData; request.HTTPBody = bodyData;
// 🧭 Debug logs // 🧭 Debug logs
NSLog(@"🌍 URL: %@", urlString); NSLog(@"🌍 URL: %@", url.absoluteString);
NSLog(@"📦 Body JSON: %@", jsonBody); NSLog(@"📦 Body JSON: %@", jsonBody);
// ✅ Make request // ✅ Make request
...@@ -1391,8 +1336,7 @@ ...@@ -1391,8 +1336,7 @@
+ (void)requestCommonArea:(void (^)(NSDictionary *data, NSError *error))completion { + (void)requestCommonArea:(void (^)(NSDictionary *data, NSError *error))completion {
// ✅ URL // ✅ URL
NSString *urlString = @"https://kitadev.commudesk.com/api/owner/plan/getCommonArea?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6ImF0aGlyYWh6YWlkaUBjb252ZXAuY29tIiwicGFzc3dvcmQiOiIkMnkkMTAkNU9xYklqMnZ6UHJwckJrcVd5c3I2LmJCc1hVYS9Hd014eUhnL3RhUUhPUkNQcGdmTG8yakciLCJzdWIiOjIxNzAsImlzcyI6Imh0dHBzOi8va2l0YWRldi5jb21tdWRlc2suY29tL2FwaS9vd25lci9hdXRoL3Bhc3N3b3JkbGVzc19sb2dpbiIsImlhdCI6MTc2MDMxNTM1MiwiZXhwIjoyMDc1ODg0ODcyLCJuYmYiOjE3NjAzMTUzNTIsImp0aSI6IktoQmNyQnJEdDVNdDZlWmMifQ.JnxGbZ7hTeoOr6oibIzZMphgyKtTo2Aq9Efx6mrGfFA"; NSURL *url = [APIConfig urlWithPath:@"/owner/plan/getCommonArea"];
NSURL *url = [NSURL URLWithString:urlString];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST"; request.HTTPMethod = @"POST";
...@@ -1401,21 +1345,12 @@ ...@@ -1401,21 +1345,12 @@
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
[request setValue:@"application/json" forHTTPHeaderField:@"Accept"]; [request setValue:@"application/json" forHTTPHeaderField:@"Accept"];
// ✅ Device info dictionary
NSDictionary *deviceInfo = @{
@"OS": @"AND",
@"IMEI": @"AND:2a2f13ff17b1cbb5",
@"OS_VERSION": @"35",
@"MODEL": @"2306EPN60G",
@"APP_VERSION": @"1.22.26"
};
// ✅ Build JSON body // ✅ Build JSON body
NSDictionary *jsonBody = @{ NSDictionary *jsonBody = @{
@"data": @{ @"data": @{
@"os": @"AND", @"os": @"AND",
@"project_id": @"8", @"project_id": @"8",
@"information": deviceInfo, @"information": [APIConfig deviceInfo],
} }
}; };
...@@ -1430,7 +1365,7 @@ ...@@ -1430,7 +1365,7 @@
request.HTTPBody = bodyData; request.HTTPBody = bodyData;
// 🧭 Debug logs // 🧭 Debug logs
NSLog(@"🌍 URL: %@", urlString); NSLog(@"🌍 URL: %@", url.absoluteString);
NSLog(@"📦 Body JSON: %@", jsonBody); NSLog(@"📦 Body JSON: %@", jsonBody);
// ✅ Make request // ✅ Make request
...@@ -1465,8 +1400,7 @@ ...@@ -1465,8 +1400,7 @@
+ (void)requestGeneralSettings:(void (^)(NSDictionary *data, NSError *error))completion { + (void)requestGeneralSettings:(void (^)(NSDictionary *data, NSError *error))completion {
// ✅ URL // ✅ URL
NSString *urlString = @"https://kitadev.commudesk.com/api/issue/getGeneralSetting?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6ImF0aGlyYWh6YWlkaUBjb252ZXAuY29tIiwicGFzc3dvcmQiOiIkMnkkMTAkNU9xYklqMnZ6UHJwckJrcVd5c3I2LmJCc1hVYS9Hd014eUhnL3RhUUhPUkNQcGdmTG8yakciLCJzdWIiOjIxNzAsImlzcyI6Imh0dHBzOi8va2l0YWRldi5jb21tdWRlc2suY29tL2FwaS9vd25lci9hdXRoL3Bhc3N3b3JkbGVzc19sb2dpbiIsImlhdCI6MTc2MDMxNTM1MiwiZXhwIjoyMDc1ODg0ODcyLCJuYmYiOjE3NjAzMTUzNTIsImp0aSI6IktoQmNyQnJEdDVNdDZlWmMifQ.JnxGbZ7hTeoOr6oibIzZMphgyKtTo2Aq9Efx6mrGfFA"; NSURL *url = [APIConfig urlWithPath:@"/owner/issue/getGeneralSetting"];
NSURL *url = [NSURL URLWithString:urlString];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST"; request.HTTPMethod = @"POST";
...@@ -1475,21 +1409,12 @@ ...@@ -1475,21 +1409,12 @@
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
[request setValue:@"application/json" forHTTPHeaderField:@"Accept"]; [request setValue:@"application/json" forHTTPHeaderField:@"Accept"];
// ✅ Device info dictionary
NSDictionary *deviceInfo = @{
@"OS": @"AND",
@"IMEI": @"AND:2a2f13ff17b1cbb5",
@"OS_VERSION": @"35",
@"MODEL": @"2306EPN60G",
@"APP_VERSION": @"1.22.26"
};
// ✅ Build JSON body // ✅ Build JSON body
NSDictionary *jsonBody = @{ NSDictionary *jsonBody = @{
@"data": @{ @"data": @{
@"os": @"AND", @"os": @"AND",
@"project_id": @"8", @"project_id": @"8",
@"information": deviceInfo, @"information": [APIConfig deviceInfo]
} }
}; };
...@@ -1504,7 +1429,7 @@ ...@@ -1504,7 +1429,7 @@
request.HTTPBody = bodyData; request.HTTPBody = bodyData;
// 🧭 Debug logs // 🧭 Debug logs
NSLog(@"🌍 URL: %@", urlString); NSLog(@"🌍 URL: %@", url.absoluteString);
NSLog(@"📦 Body JSON: %@", jsonBody); NSLog(@"📦 Body JSON: %@", jsonBody);
// ✅ Make request // ✅ Make request
...@@ -1539,8 +1464,7 @@ ...@@ -1539,8 +1464,7 @@
+ (void)requestDefectMatrix:(void (^)(NSDictionary *data, NSError *error))completion { + (void)requestDefectMatrix:(void (^)(NSDictionary *data, NSError *error))completion {
// ✅ URL // ✅ URL
NSString *urlString = @"https://kitadev.commudesk.com/api/issue/getGeneralSettingByUnit?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6ImF0aGlyYWh6YWlkaUBjb252ZXAuY29tIiwicGFzc3dvcmQiOiIkMnkkMTAkNU9xYklqMnZ6UHJwckJrcVd5c3I2LmJCc1hVYS9Hd014eUhnL3RhUUhPUkNQcGdmTG8yakciLCJzdWIiOjIxNzAsImlzcyI6Imh0dHBzOi8va2l0YWRldi5jb21tdWRlc2suY29tL2FwaS9vd25lci9hdXRoL3Bhc3N3b3JkbGVzc19sb2dpbiIsImlhdCI6MTc2MDMxNTM1MiwiZXhwIjoyMDc1ODg0ODcyLCJuYmYiOjE3NjAzMTUzNTIsImp0aSI6IktoQmNyQnJEdDVNdDZlWmMifQ.JnxGbZ7hTeoOr6oibIzZMphgyKtTo2Aq9Efx6mrGfFA"; NSURL *url = [APIConfig urlWithPath:@"/issue/getGeneralSettingByUnit"];
NSURL *url = [NSURL URLWithString:urlString];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST"; request.HTTPMethod = @"POST";
...@@ -1549,22 +1473,13 @@ ...@@ -1549,22 +1473,13 @@
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
[request setValue:@"application/json" forHTTPHeaderField:@"Accept"]; [request setValue:@"application/json" forHTTPHeaderField:@"Accept"];
// ✅ Device info dictionary
NSDictionary *deviceInfo = @{
@"OS": @"AND",
@"IMEI": @"AND:2a2f13ff17b1cbb5",
@"OS_VERSION": @"35",
@"MODEL": @"2306EPN60G",
@"APP_VERSION": @"1.22.26"
};
// ✅ Build JSON body // ✅ Build JSON body
NSDictionary *jsonBody = @{ NSDictionary *jsonBody = @{
@"data": @{ @"data": @{
@"os": @"AND", @"os": @"AND",
@"project_id": @"8", @"project_id": @"8",
@"drawing_plan_id": @"135", @"drawing_plan_id": @"135",
@"information": deviceInfo, @"information": [APIConfig deviceInfo]
} }
}; };
...@@ -1579,7 +1494,7 @@ ...@@ -1579,7 +1494,7 @@
request.HTTPBody = bodyData; request.HTTPBody = bodyData;
// 🧭 Debug logs // 🧭 Debug logs
NSLog(@"🌍 URL: %@", urlString); NSLog(@"🌍 URL: %@", url.absoluteString);
NSLog(@"📦 Body JSON: %@", jsonBody); NSLog(@"📦 Body JSON: %@", jsonBody);
// ✅ Make request // ✅ Make request
...@@ -1614,8 +1529,7 @@ ...@@ -1614,8 +1529,7 @@
+ (void)requestProjectList:(void (^)(NSDictionary *data, NSError *error))completion { + (void)requestProjectList:(void (^)(NSDictionary *data, NSError *error))completion {
// ✅ URL // ✅ URL
NSString *urlString = @"https://kitadev.commudesk.com/api/owner/project/listProject?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6ImF0aGlyYWh6YWlkaUBjb252ZXAuY29tIiwicGFzc3dvcmQiOiIkMnkkMTAkNU9xYklqMnZ6UHJwckJrcVd5c3I2LmJCc1hVYS9Hd014eUhnL3RhUUhPUkNQcGdmTG8yakciLCJzdWIiOjIxNzAsImlzcyI6Imh0dHBzOi8va2l0YWRldi5jb21tdWRlc2suY29tL2FwaS9vd25lci9hdXRoL3Bhc3N3b3JkbGVzc19sb2dpbiIsImlhdCI6MTc2MDMxNTM1MiwiZXhwIjoyMDc1ODg0ODcyLCJuYmYiOjE3NjAzMTUzNTIsImp0aSI6IktoQmNyQnJEdDVNdDZlWmMifQ.JnxGbZ7hTeoOr6oibIzZMphgyKtTo2Aq9Efx6mrGfFA"; NSURL *url = [APIConfig urlWithPath:@"/owner/project/listProject"];
NSURL *url = [NSURL URLWithString:urlString];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST"; request.HTTPMethod = @"POST";
...@@ -1642,7 +1556,7 @@ ...@@ -1642,7 +1556,7 @@
request.HTTPBody = bodyData; request.HTTPBody = bodyData;
// 🧭 Debug logs // 🧭 Debug logs
NSLog(@"🌍 URL: %@", urlString); NSLog(@"🌍 URL: %@", url.absoluteString);
NSLog(@"📦 Body JSON: %@", jsonBody); NSLog(@"📦 Body JSON: %@", jsonBody);
// ✅ Make request // ✅ Make request
...@@ -1679,8 +1593,7 @@ ...@@ -1679,8 +1593,7 @@
completion:(void (^)(NSDictionary *data, NSError *error))completion { completion:(void (^)(NSDictionary *data, NSError *error))completion {
// ✅ URL // ✅ URL
NSString *urlString = @"https://kitadev.commudesk.com/api/owner/offline/syncImageArray?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6ImF0aGlyYWh6YWlkaUBjb252ZXAuY29tIiwicGFzc3dvcmQiOiIkMnkkMTAkNU9xYklqMnZ6UHJwckJrcVd5c3I2LmJCc1hVYS9Hd014eUhnL3RhUUhPUkNQcGdmTG8yakciLCJzdWIiOjIxNzAsImlzcyI6Imh0dHBzOi8va2l0YWRldi5jb21tdWRlc2suY29tL2FwaS9vd25lci9hdXRoL3Bhc3N3b3JkbGVzc19sb2dpbiIsImlhdCI6MTc2MDMxNTM1MiwiZXhwIjoyMDc1ODg0ODcyLCJuYmYiOjE3NjAzMTUzNTIsImp0aSI6IktoQmNyQnJEdDVNdDZlWmMifQ.JnxGbZ7hTeoOr6oibIzZMphgyKtTo2Aq9Efx6mrGfFA"; NSURL *url = [APIConfig urlWithPath:@"/owner/offline/syncImageArray"];
NSURL *url = [NSURL URLWithString:urlString];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST"; request.HTTPMethod = @"POST";
...@@ -1705,7 +1618,7 @@ ...@@ -1705,7 +1618,7 @@
request.HTTPBody = bodyData; request.HTTPBody = bodyData;
// 🧭 Debug logs // 🧭 Debug logs
NSLog(@"🌍 URL: %@", urlString); NSLog(@"🌍 URL: %@", url.absoluteString);
NSLog(@"📦 Body JSON: %@", jsonBody); NSLog(@"📦 Body JSON: %@", jsonBody);
// ✅ Make request // ✅ Make request
...@@ -1740,42 +1653,42 @@ ...@@ -1740,42 +1653,42 @@
// + (void)downloadAndCacheImage:(NSURL *)url // + (void)downloadAndCacheImage:(NSURL *)url
// completion:(void (^)(BOOL success))completion { // completion:(void (^)(BOOL success))completion {
//
// if (!url) { // if (!url) {
// if (completion) completion(NO); // if (completion) completion(NO);
// return; // return;
// } // }
//
// // 1️⃣ Build cache path // // 1️⃣ Build cache path
// NSString *fileName = url.lastPathComponent; // NSString *fileName = url.lastPathComponent;
// // NSString *cacheDir = [self offlineImageCacheDirectory]; // // NSString *cacheDir = [self offlineImageCacheDirectory];
// NSString *filePath = [cacheDir stringByAppendingPathComponent:fileName]; // NSString *filePath = [cacheDir stringByAppendingPathComponent:fileName];
//
// // 2️⃣ Skip if already cached // // 2️⃣ Skip if already cached
// if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) { // if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
// NSLog(@"🟢 Cached file exists: %@", fileName); // NSLog(@"🟢 Cached file exists: %@", fileName);
// if (completion) completion(YES); // if (completion) completion(YES);
// return; // return;
// } // }
//
// // 3️⃣ Build request with timeout // // 3️⃣ Build request with timeout
// NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; // NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
// request.HTTPMethod = @"GET"; // request.HTTPMethod = @"GET";
// request.timeoutInterval = 20; // ⏱️ per-file timeout // request.timeoutInterval = 20; // ⏱️ per-file timeout
//
// NSURLSessionConfiguration *config = // NSURLSessionConfiguration *config =
// [NSURLSessionConfiguration defaultSessionConfiguration]; // [NSURLSessionConfiguration defaultSessionConfiguration];
// config.requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData; // config.requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
//
// NSURLSession *session = [NSURLSession sessionWithConfiguration:config]; // NSURLSession *session = [NSURLSession sessionWithConfiguration:config];
//
// // 4️⃣ Download task // // 4️⃣ Download task
// NSURLSessionDownloadTask *task = // NSURLSessionDownloadTask *task =
// [session downloadTaskWithRequest:request // [session downloadTaskWithRequest:request
// completionHandler:^(NSURL *location, // completionHandler:^(NSURL *location,
// NSURLResponse *response, // NSURLResponse *response,
// NSError *error) { // NSError *error) {
//
// if (error || !location) { // if (error || !location) {
// NSLog(@"❌ Download failed: %@ | %@", url, error); // NSLog(@"❌ Download failed: %@ | %@", url, error);
// dispatch_async(dispatch_get_main_queue(), ^{ // dispatch_async(dispatch_get_main_queue(), ^{
...@@ -1783,14 +1696,14 @@ ...@@ -1783,14 +1696,14 @@
// }); // });
// return; // return;
// } // }
//
// // 5️⃣ Move file to cache // // 5️⃣ Move file to cache
// NSError *moveError; // NSError *moveError;
// [[NSFileManager defaultManager] // [[NSFileManager defaultManager]
// moveItemAtURL:location // moveItemAtURL:location
// toURL:[NSURL fileURLWithPath:filePath] // toURL:[NSURL fileURLWithPath:filePath]
// error:&moveError]; // error:&moveError];
//
// if (moveError) { // if (moveError) {
// NSLog(@"❌ Save failed: %@", moveError); // NSLog(@"❌ Save failed: %@", moveError);
// dispatch_async(dispatch_get_main_queue(), ^{ // dispatch_async(dispatch_get_main_queue(), ^{
...@@ -1798,14 +1711,14 @@ ...@@ -1798,14 +1711,14 @@
// }); // });
// return; // return;
// } // }
//
// NSLog(@"✅ Cached: %@", fileName); // NSLog(@"✅ Cached: %@", fileName);
//
// dispatch_async(dispatch_get_main_queue(), ^{ // dispatch_async(dispatch_get_main_queue(), ^{
// if (completion) completion(YES); // if (completion) completion(YES);
// }); // });
// }]; // }];
//
// [task resume]; // [task resume];
// } // }
......
// APIConfig.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface APIConfig : NSObject
/// Base API URL (no trailing slash)
+ (NSString *)baseURL;
/// Auth token (JWT)
+ (NSString *)authToken;
/// Convenience: baseURL + path + ?token=
+ (NSURL *)urlWithPath:(NSString *)path;
/// Dictionary form (for future flexibility)
+ (NSDictionary<NSString *, NSString *> *)deviceInfo;
/// JSON string form (what your APIs expect)
+ (NSString *)deviceInfoJSONString;
@end
NS_ASSUME_NONNULL_END
// APIConfig.mm
#import "APIConfig.h"
@implementation APIConfig
+ (NSString *)baseURL {
return @"https://kitadev.commudesk.com/api";
}
+ (NSString *)authToken {
return @"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6ImF0aGlyYWh6YWlkaUBjb252ZXAuY29tIiwicGFzc3dvcmQiOiIkMnkkMTAkNU9xYklqMnZ6UHJwckJrcVd5c3I2LmJCc1hVYS9Hd014eUhnL3RhUUhPUkNQcGdmTG8yakciLCJzdWIiOjIxNzAsImlzcyI6Imh0dHBzOi8va2l0YWRldi5jb21tdWRlc2suY29tL2FwaS9vd25lci9hdXRoL3Bhc3N3b3JkbGVzc19sb2dpbiIsImlhdCI6MTc2MDMxNTM1MiwiZXhwIjoyMDc1ODg0ODcyLCJuYmYiOjE3NjAzMTUzNTIsImp0aSI6IktoQmNyQnJEdDVNdDZlWmMifQ.JnxGbZ7hTeoOr6oibIzZMphgyKtTo2Aq9Efx6mrGfFA";
}
+ (NSURL *)urlWithPath:(NSString *)path {
NSString *urlString =
[NSString stringWithFormat:@"%@%@?token=%@",
[self baseURL],
path,
[self authToken]];
return [NSURL URLWithString:urlString];
}
+ (NSDictionary<NSString *, NSString *> *)deviceInfo {
return @{
@"OS": @"AND",
@"IMEI": @"AND:2a2f13ff17b1cbb5",
@"OS_VERSION": @"35",
@"MODEL": @"2306EPN60G",
@"APP_VERSION": @"1.22.26"
};
}
+ (NSString *)deviceInfoJSONString {
NSData *data =
[NSJSONSerialization dataWithJSONObject:[self deviceInfo]
options:0
error:nil];
return [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
}
@end
...@@ -169,20 +169,24 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE"; ...@@ -169,20 +169,24 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE";
__block NSInteger step = 0; __block NSInteger step = 0;
__weak typeof(self) weakSelf = self; __weak typeof(self) weakSelf = self;
__block void (^processNext)(void); // 🔑 MUST be __block
__weak __block void (^weakProcessNext)(void); __block void (^processNextItem)(void);
processNext = ^{ processNextItem = ^{
__strong typeof(weakSelf) strongSelf = weakSelf; __strong typeof(weakSelf) strongSelf = weakSelf;
if (!strongSelf) return; if (!strongSelf) return;
NSMutableArray *currentQueue = [strongSelf safeLoadQueue]; if (queue.count == 0) {
if (currentQueue.count == 0) { NSLog(@"✅ [OfflineSync] Queue empty — finishing");
if (completion) completion(YES); if (completion) completion(YES);
// 🔑 BREAK the self-retain cycle
processNextItem = nil;
return; return;
} }
NSDictionary *item = currentQueue.firstObject; NSDictionary *item = queue.firstObject;
step++; step++;
if (progress) { if (progress) {
...@@ -194,14 +198,17 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE"; ...@@ -194,14 +198,17 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE";
[strongSelf safeProcessItem:item taskKey:taskKey completion:^(BOOL success) { [strongSelf safeProcessItem:item taskKey:taskKey completion:^(BOOL success) {
__strong typeof(weakSelf) strongSelf2 = weakSelf;
if (!strongSelf2) return;
// Save to history // Save to history
NSMutableDictionary *historyItem = [item mutableCopy]; NSMutableDictionary *historyItem = [item mutableCopy];
historyItem[@"syncedAt"] = [strongSelf todayDateString]; historyItem[@"syncedAt"] = [strongSelf2 todayDateString];
historyItem[@"status"] = success ? @"SUCCESS" : @"FAILED"; historyItem[@"status"] = success ? @"SUCCESS" : @"FAILED";
NSMutableArray *history = [strongSelf safeLoadHistory]; NSMutableArray *history = [strongSelf2 safeLoadHistory];
[history insertObject:historyItem atIndex:0]; [history insertObject:historyItem atIndex:0];
[strongSelf saveHistoryQueue:history]; [strongSelf2 saveHistoryQueue:history];
// Cleanup files // Cleanup files
for (NSString *path in item[@"imagePaths"]) { for (NSString *path in item[@"imagePaths"]) {
...@@ -211,22 +218,21 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE"; ...@@ -211,22 +218,21 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE";
[[NSFileManager defaultManager] removeItemAtPath:item[@"mediaPath"] error:nil]; [[NSFileManager defaultManager] removeItemAtPath:item[@"mediaPath"] error:nil];
} }
// Remove processed item from queue // Remove item
NSMutableArray *updatedQueue = [strongSelf safeLoadQueue]; [queue removeObjectAtIndex:0];
if (updatedQueue.count > 0) { [strongSelf2 saveQueue:queue];
[updatedQueue removeObjectAtIndex:0];
[strongSelf saveQueue:updatedQueue];
}
// Move to next item // Next iteration
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
if (weakProcessNext) weakProcessNext(); if (processNextItem) {
processNextItem();
}
}); });
}]; }];
}; };
weakProcessNext = processNext; // Start
processNext(); processNextItem();
} }
- (void)safeProcessItem:(NSDictionary *)item - (void)safeProcessItem:(NSDictionary *)item
...@@ -545,22 +551,30 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE"; ...@@ -545,22 +551,30 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE";
NSString *drawingPlanId = @"135"; NSString *drawingPlanId = @"135";
NSArray *selectedUnits = [LocalStorage loadArrayForKey:kSelectedOfflineUnitsKey] ?: @[]; NSArray *selectedUnits = [LocalStorage loadArrayForKey:kSelectedOfflineUnitsKey] ?: @[];
NSMutableArray *tasks = [NSMutableArray array];
// ------------------------------ // ------------------------------
// Task 1 — requestIssues // Build sequential tasks
// ------------------------------ // ------------------------------
NSMutableArray<void (^)(void (^next)(void))> *tasks = [NSMutableArray array];
// Task 1 — requestIssues
[tasks addObject:^(void (^next)(void)) { [tasks addObject:^(void (^next)(void)) {
[self safeSequentialTask:@"task1" block:^(void (^safeNext)(void), void (^logSuccess)(BOOL)) { [self safeSequentialTask:@"task1" block:^(void (^safeNext)(void), void (^logSuccess)(BOOL)) {
NSString *key = [NSString stringWithFormat:@"ISSUES_%@", drawingPlanId]; NSString *key = [NSString stringWithFormat:@"ISSUES_%@", drawingPlanId];
NSLog(@"🚀 Starting Task 1 — requestIssues (key: %@)", key);
[APIClient requestIssues:^(NSDictionary *res, NSError *err) { [APIClient requestIssues:^(NSDictionary *res, NSError *err) {
NSString *logText = [NSString stringWithFormat:
@"\n🚀 Task 1 — requestIssues\nTime: %@\nError: %@\nResponse: %@\n",
[NSDate date], err, res];
[self appendOfflineLog:logText];
if (err) { if (err) {
NSLog(@"⚠️ Task 1 failed: %@", err); NSLog(@"⚠️ Task 1 failed: %@", err);
logSuccess(NO); logSuccess(NO);
} else { } else {
NSLog(@"✅ Task 1 succeeded");
if (res) [LocalStorage saveDictionary:res[@"Data"] forKey:key]; if (res) [LocalStorage saveDictionary:res[@"Data"] forKey:key];
NSLog(@"✅ Task 1 succeeded, saved to key: %@", key);
logSuccess(YES); logSuccess(YES);
} }
safeNext(); safeNext();
...@@ -569,20 +583,24 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE"; ...@@ -569,20 +583,24 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE";
} next:next]; } next:next];
}]; }];
// ------------------------------
// Task 2 — requestDashboardInfo // Task 2 — requestDashboardInfo
// ------------------------------
[tasks addObject:^(void (^next)(void)) { [tasks addObject:^(void (^next)(void)) {
[self safeSequentialTask:@"task2" block:^(void (^safeNext)(void), void (^logSuccess)(BOOL)) { [self safeSequentialTask:@"task2" block:^(void (^safeNext)(void), void (^logSuccess)(BOOL)) {
NSString *key = [NSString stringWithFormat:@"DASHBOARD_INFO_%@", drawingPlanId]; NSString *key = [NSString stringWithFormat:@"DASHBOARD_INFO_%@", drawingPlanId];
NSLog(@"🚀 Starting Task 2 — requestDashboardInfo (key: %@)", key);
[APIClient requestDashboardInfo:^(NSDictionary *res, NSError *err) { [APIClient requestDashboardInfo:^(NSDictionary *res, NSError *err) {
NSString *logText = [NSString stringWithFormat:
@"\n🚀 Task 2 — requestDashboardInfo\nTime: %@\nError: %@\nResponse: %@\n",
[NSDate date], err, res];
[self appendOfflineLog:logText];
if (err) { if (err) {
NSLog(@"⚠️ Task 2 failed: %@", err); NSLog(@"⚠️ Task 2 failed: %@", err);
logSuccess(NO); logSuccess(NO);
} else { } else {
NSLog(@"✅ Task 2 succeeded");
if (res) [LocalStorage saveDictionary:res[@"Data"] forKey:key]; if (res) [LocalStorage saveDictionary:res[@"Data"] forKey:key];
NSLog(@"✅ Task 2 succeeded, saved to key: %@", key);
logSuccess(YES); logSuccess(YES);
} }
safeNext(); safeNext();
...@@ -591,81 +609,108 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE"; ...@@ -591,81 +609,108 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE";
} next:next]; } next:next];
}]; }];
// ------------------------------ // Task 3 — requestGetUnitPlan (already has logs)
// Task 3 — requestGetUnitPlan
// ------------------------------
[tasks addObject:^(void (^next)(void)) { [tasks addObject:^(void (^next)(void)) {
[self safeSequentialTask:@"task3" block:^(void (^safeNext)(void), void (^logSuccess)(BOOL)) { [self safeSequentialTask:@"task3" block:^(void (^safeNext)(void), void (^logSuccess)(BOOL)) {
NSString *key = [NSString stringWithFormat:@"PLAN_UNIT_%@", drawingPlanId]; NSString *key = [NSString stringWithFormat:@"PLAN_UNIT_%@", drawingPlanId];
NSLog(@"🚀 Starting Task 3 — requestGetUnitPlan (key: %@)", key);
[APIClient requestGetUnitPlan:^(NSDictionary *res, NSError *err) { [APIClient requestGetUnitPlan:^(NSDictionary *res, NSError *err) {
NSString *logText = [NSString stringWithFormat:
@"\n🚀 Task 3 — requestGetUnitPlan\nTime: %@\nError: %@\nResponse: %@\n",
[NSDate date], err, res];
[self appendOfflineLog:logText];
if (err || ![res isKindOfClass:[NSDictionary class]]) { if (err || ![res isKindOfClass:[NSDictionary class]]) {
NSLog(@"⚠️ Task 3 failed: %@", err); NSLog(@"⚠️ Task 3 failed: %@", err);
logSuccess(NO); logSuccess(NO);
} else { safeNext();
return;
}
id dataArray = res[@"Data"]; id dataArray = res[@"Data"];
NSDictionary *objData = @{}; NSDictionary *objData = @{};
if ([dataArray isKindOfClass:[NSArray class]] && [dataArray count] > 0) { if ([dataArray isKindOfClass:[NSArray class]] && ((NSArray *)dataArray).count > 0) {
id firstItem = dataArray[0]; id firstItem = ((NSArray *)dataArray)[0];
if ([firstItem isKindOfClass:[NSDictionary class]]) objData = firstItem; if ([firstItem isKindOfClass:[NSDictionary class]]) objData = firstItem;
} }
[LocalStorage saveDictionary:objData forKey:key]; [LocalStorage saveDictionary:objData forKey:key];
NSLog(@"✅ Task 3 succeeded, saved to key: %@", key);
logSuccess(YES); logSuccess(YES);
}
safeNext(); safeNext();
}]; }];
} next:next]; } next:next];
}]; }];
// ------------------------------
// Task 4 — requestGeneralSettings // Task 4 — requestGeneralSettings
// ------------------------------
[tasks addObject:^(void (^next)(void)) { [tasks addObject:^(void (^next)(void)) {
[self safeSequentialTask:@"task4" block:^(void (^safeNext)(void), void (^logSuccess)(BOOL)) { [self safeSequentialTask:@"task4" block:^(void (^safeNext)(void), void (^logSuccess)(BOOL)) {
NSString *key = [NSString stringWithFormat:@"SETTINGS_%@", projectId]; NSString *key = [NSString stringWithFormat:@"SETTINGS_%@", projectId];
NSLog(@"🚀 Starting Task 4 — requestGeneralSettings (key: %@)", key);
[APIClient requestGeneralSettings:^(NSDictionary *res, NSError *err) { [APIClient requestGeneralSettings:^(NSDictionary *res, NSError *err) {
NSString *logText = [NSString stringWithFormat:
@"\n🚀 Task 4 — requestGeneralSettings\nTime: %@\nError: %@\nResponse: %@\n",
[NSDate date], err, res];
[self appendOfflineLog:logText];
if (err) { if (err) {
NSLog(@"⚠️ Task 4 failed: %@", err); NSLog(@"⚠️ Task 4 failed: %@", err);
logSuccess(NO); logSuccess(NO);
} else { } else {
if (res) [LocalStorage saveDictionary:res[@"Data"] forKey:key]; if (res) [LocalStorage saveDictionary:res[@"Data"] forKey:key];
NSLog(@"✅ Task 4 succeeded, saved to key: %@", key);
logSuccess(YES); logSuccess(YES);
NSLog(@"✅ Task 4 succeeded");
} }
safeNext(); safeNext();
}]; }];
} next:next]; } next:next];
}]; }];
// ------------------------------
// Task 5 — requestCommonArea // Task 5 — requestCommonArea
// ------------------------------
[tasks addObject:^(void (^next)(void)) { [tasks addObject:^(void (^next)(void)) {
[self safeSequentialTask:@"task5" block:^(void (^safeNext)(void), void (^logSuccess)(BOOL)) { [self safeSequentialTask:@"task5" block:^(void (^safeNext)(void), void (^logSuccess)(BOOL)) {
NSString *key = [NSString stringWithFormat:@"COMMONAREA_%@", projectId]; NSString *key = [NSString stringWithFormat:@"COMMONAREA_%@", projectId];
NSLog(@"🚀 Starting Task 5 — requestCommonArea (key: %@)", key);
[APIClient requestCommonArea:^(NSDictionary *res, NSError *err) { [APIClient requestCommonArea:^(NSDictionary *res, NSError *err) {
NSString *logText = [NSString stringWithFormat:
@"\n🚀 Task 5 — requestCommonArea\nTime: %@\nError: %@\nResponse: %@\n",
[NSDate date], err, res];
[self appendOfflineLog:logText];
if (err) { if (err) {
NSLog(@"⚠️ Task 5 failed: %@", err); NSLog(@"⚠️ Task 5 failed: %@", err);
logSuccess(NO); logSuccess(NO);
} else { } else {
if (res) [LocalStorage saveDictionary:res[@"Data"] forKey:key]; if (res) [LocalStorage saveDictionary:res[@"Data"] forKey:key];
NSLog(@"✅ Task 5 succeeded, saved to key: %@", key);
logSuccess(YES); logSuccess(YES);
NSLog(@"✅ Task 5 succeeded");
} }
safeNext(); safeNext();
}]; }];
} next:next]; } next:next];
}]; }];
// ------------------------------
// Task 6 — requestDefectMatrix // Task 6 — requestDefectMatrix
// ------------------------------
[tasks addObject:^(void (^next)(void)) { [tasks addObject:^(void (^next)(void)) {
[self safeSequentialTask:@"task6" block:^(void (^safeNext)(void), void (^logSuccess)(BOOL)) { [self safeSequentialTask:@"task6" block:^(void (^safeNext)(void), void (^logSuccess)(BOOL)) {
NSLog(@"🚀 Starting Task 6 — requestDefectMatrix");
[APIClient requestDefectMatrix:^(NSDictionary *res, NSError *err) { [APIClient requestDefectMatrix:^(NSDictionary *res, NSError *err) {
NSString *logText = [NSString stringWithFormat:
@"\n🚀 Task 6 — requestDefectMatrix\nTime: %@\nError: %@\nResponse: %@\n",
[NSDate date], err, res];
[self appendOfflineLog:logText];
if (!res || err) { if (!res || err) {
NSLog(@"⚠️ Task 6 failed: %@", err); NSLog(@"⚠️ Task 6 failed: %@", err);
logSuccess(NO); logSuccess(NO);
...@@ -675,6 +720,7 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE"; ...@@ -675,6 +720,7 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE";
NSDictionary *data = res[@"Data"]; NSDictionary *data = res[@"Data"];
if (![data isKindOfClass:[NSDictionary class]]) { if (![data isKindOfClass:[NSDictionary class]]) {
NSLog(@"⚠️ Task 6 failed: Data invalid");
logSuccess(NO); logSuccess(NO);
safeNext(); safeNext();
return; return;
...@@ -709,140 +755,114 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE"; ...@@ -709,140 +755,114 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE";
for (id locationId in locationIds) { for (id locationId in locationIds) {
NSString *locationKey = [NSString stringWithFormat:@"LOCATION_SETTINGS_%@", locationId]; NSString *locationKey = [NSString stringWithFormat:@"LOCATION_SETTINGS_%@", locationId];
[LocalStorage saveDictionary:settings forKey:locationKey]; [LocalStorage saveDictionary:settings forKey:locationKey];
NSLog(@"✅ Task 6 — saved settings for location: %@", locationId);
} }
} }
} }
NSLog(@"✅ Task 6 succeeded");
logSuccess(YES); logSuccess(YES);
safeNext(); safeNext();
}]; }];
} next:next]; } next:next];
}]; }];
// ------------------------------ // Task 7 — Image downloads
// Task 7 — Download Images if (selectedUnits.count > 0) {
// ------------------------------
[tasks addObject:^(void (^next)(void)) {
[self safeSequentialTask:@"task7" block:^(void (^safeNext)(void), void (^logSuccess)(BOOL)) {
if (selectedUnits.count == 0) {
logSuccess(YES);
safeNext();
return;
}
[self fetchOfflineImageListForSelectedUnits:selectedUnits completion:^(NSArray<NSURL *> *urls, NSError *error) { [self fetchOfflineImageListForSelectedUnits:selectedUnits completion:^(NSArray<NSURL *> *urls, NSError *error) {
if (error || urls.count == 0) { if (error || urls.count == 0) {
logSuccess(NO); NSLog(@"⚠️ No images to download");
safeNext(); } else {
return; // Flatten all images into tasks
for (NSInteger i = 0; i < urls.count; i++) {
NSURL *url = urls[i];
[tasks addObject:^(void (^next)(void)) {
NSString *urlKey = [NSString stringWithFormat:@"image_%ld", (long)i];
__block BOOL finished = NO;
// Timeout guard per image
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
if (!finished) {
NSLog(@"⚠️ Image download timeout: %@", url);
NSMutableDictionary *status =
[[LocalStorage loadDictionaryForKey:@"OFFLINE_DOWNLOAD_STATUS"] mutableCopy] ?: [NSMutableDictionary dictionary];
status[urlKey] = @"FAILED";
[LocalStorage saveDictionary:status forKey:@"OFFLINE_DOWNLOAD_STATUS"];
finished = YES;
if (next) next();
} }
});
[self downloadImagesSequentially:urls [APIClient downloadAndCacheImage:url completion:^(BOOL success) {
progress:^(NSInteger completedImages, NSInteger totalImages) { if (finished) return;
NSInteger overallCompleted = 6 + completedImages; finished = YES;
NSInteger overallTotal = 6 + urls.count; NSMutableDictionary *status =
if (progress) progress(overallCompleted, overallTotal); [[LocalStorage loadDictionaryForKey:@"OFFLINE_DOWNLOAD_STATUS"] mutableCopy] ?: [NSMutableDictionary dictionary];
} next:^{ status[urlKey] = success ? @"SUCCESS" : @"FAILED";
logSuccess(YES); [LocalStorage saveDictionary:status forKey:@"OFFLINE_DOWNLOAD_STATUS"];
safeNext(); if (next) next();
}];
}]; }];
} next:next];
}]; }];
}
}
// ------------------------------ // ------------------------------
// Sequential executor // Start recursive execution
// ------------------------------ // ------------------------------
__block NSInteger currentTaskIndex = 0; __block NSInteger currentIndex = 0;
NSInteger totalTasks = tasks.count;
__block void (^runNextTask)(void) = nil;
__weak typeof(runNextTask) weakRunNextTask = nil;
__block void (^runNextTask)(void);
runNextTask = ^{ runNextTask = ^{
__strong typeof(weakRunNextTask) strongRunNextTask = weakRunNextTask; if (currentIndex >= totalTasks) {
if (!strongRunNextTask) return;
if (currentTaskIndex >= tasks.count) {
[self saveLastOfflineSyncDate]; [self saveLastOfflineSyncDate];
[self saveOfflineDownloadStatusToFile];
if (completion) completion(YES); if (completion) completion(YES);
runNextTask = nil;
return; return;
} }
void (^task)(void (^)(void)) = tasks[currentTaskIndex]; void (^task)(void (^)(void)) = tasks[currentIndex];
currentTaskIndex++; currentIndex++;
if (progress && currentTaskIndex <= 6) { if (progress) progress(currentIndex, totalTasks);
NSInteger overallTotal = 6 + selectedUnits.count;
progress(currentTaskIndex, overallTotal);
}
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
task(strongRunNextTask); task(runNextTask);
}); });
}; };
weakRunNextTask = runNextTask;
runNextTask(); runNextTask();
} }];
#pragma mark - Robust Image Download
- (void)downloadImagesSequentially:(NSArray<NSURL *> *)urls
progress:(void (^)(NSInteger completed, NSInteger total))progress
next:(void (^)(void))next {
if (urls.count == 0) {
if (next) next();
return; return;
} }
__block NSInteger index = 0; // If no selected units, just run tasks 1–6
NSInteger total = urls.count; __block NSInteger currentIndex = 0;
NSInteger totalTasks = tasks.count;
__block void (^downloadNext)(void) = nil;
__weak __block void (^weakDownloadNext)(void) = nil;
downloadNext = ^{ __block void (^runNextTask)(void);
__strong typeof(weakDownloadNext) strongDownloadNext = weakDownloadNext; runNextTask = ^{
if (!strongDownloadNext) return; if (currentIndex >= totalTasks) {
[self saveLastOfflineSyncDate];
if (index >= total) { [self saveOfflineDownloadStatusToFile];
if (next) next(); if (completion) completion(YES);
runNextTask = nil;
return; return;
} }
NSURL *url = urls[index]; void (^task)(void (^)(void)) = tasks[currentIndex];
NSString *urlKey = [NSString stringWithFormat:@"image_%ld", (long)index]; currentIndex++;
index++;
// Timeout guard
__block BOOL finished = NO;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
if (!finished) {
NSLog(@"⚠️ Image download timeout: %@", url);
NSMutableDictionary *status = [[LocalStorage loadDictionaryForKey:@"OFFLINE_DOWNLOAD_STATUS"] mutableCopy] ?: [NSMutableDictionary dictionary];
status[urlKey] = @"FAILED";
[LocalStorage saveDictionary:status forKey:@"OFFLINE_DOWNLOAD_STATUS"];
finished = YES;
if (progress) progress(index, total);
strongDownloadNext();
}
});
[APIClient downloadAndCacheImage:url completion:^(BOOL success) {
if (finished) return;
NSMutableDictionary *status = [[LocalStorage loadDictionaryForKey:@"OFFLINE_DOWNLOAD_STATUS"] mutableCopy] ?: [NSMutableDictionary dictionary]; if (progress) progress(currentIndex, totalTasks);
status[urlKey] = success ? @"SUCCESS" : @"FAILED";
[LocalStorage saveDictionary:status forKey:@"OFFLINE_DOWNLOAD_STATUS"];
finished = YES;
if (progress) progress(index, total); dispatch_async(dispatch_get_main_queue(), ^{
strongDownloadNext(); task(runNextTask);
}]; });
}; };
weakDownloadNext = downloadNext; runNextTask();
downloadNext();
} }
#pragma mark - Image Download Helpers #pragma mark - Image Download Helpers
...@@ -985,5 +1005,49 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE"; ...@@ -985,5 +1005,49 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE";
NSLog(@"============================================\n"); NSLog(@"============================================\n");
} }
- (void)saveOfflineDownloadStatusToFile {
NSMutableDictionary *status = [[LocalStorage loadDictionaryForKey:@"OFFLINE_DOWNLOAD_STATUS"] mutableCopy] ?: [NSMutableDictionary dictionary];
NSError *error;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:status options:NSJSONWritingPrettyPrinted error:&error];
if (!jsonData) {
NSLog(@"⚠️ Failed to serialize offline download status: %@", error);
return;
}
NSString *documentsDir = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
NSString *filePath = [documentsDir stringByAppendingPathComponent:@"OfflineDownloadStatus.txt"];
BOOL success = [jsonData writeToFile:filePath options:NSDataWritingAtomic error:&error];
if (success) {
NSLog(@"✅ Offline download status saved to file: %@", filePath);
} else {
NSLog(@"⚠️ Failed to write offline download status to file: %@", error);
}
}
- (NSString *)offlineLogFilePath {
NSString *docsDir = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
NSString *filePath = [docsDir stringByAppendingPathComponent:@"OfflineSyncLog.txt"];
NSLog(@"📄 Offline log file path: %@", filePath); // <-- added log
return filePath;
}
- (void)appendOfflineLog:(NSString *)text {
NSString *filePath = [self offlineLogFilePath];
NSFileManager *fm = [NSFileManager defaultManager];
if (![fm fileExistsAtPath:filePath]) {
[text writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:nil];
NSLog(@"🆕 Created offline log file at path: %@", filePath); // log when file created
} else {
NSFileHandle *handle = [NSFileHandle fileHandleForWritingAtPath:filePath];
[handle seekToEndOfFile];
[handle writeData:[text dataUsingEncoding:NSUTF8StringEncoding]];
[handle closeFile];
NSLog(@"✏️ Appended to offline log file: %@", filePath); // log when appending
}
}
@end @end
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment