Commit 6da6b554 authored by Wei Han's avatar Wei Han

code update

parent 9e06acac
...@@ -8,10 +8,10 @@ ...@@ -8,10 +8,10 @@
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
21E89413E97A865028E9F8E7 /* libPods-example.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9B670364245CE8751F827745 /* libPods-example.a */; };
44006126336D83DB220E7214 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */; }; 44006126336D83DB220E7214 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */; };
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 */; };
92F3BF4D0A6D1113D359EFA3 /* libPods-example.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 358AE48123A5C7B504741D4F /* libPods-example.a */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
...@@ -19,12 +19,12 @@ ...@@ -19,12 +19,12 @@
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>"; };
521745100EAB4A9BA708C727 /* 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>"; }; 358AE48123A5C7B504741D4F /* libPods-example.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-example.a"; sourceTree = BUILT_PRODUCTS_DIR; };
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>"; };
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>"; };
9B670364245CE8751F827745 /* libPods-example.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-example.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 9240166696F5B2EC6A36F688 /* 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>"; };
950B7AF3DD3B011B04438348 /* 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>"; };
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; };
F54AF3C297616CFF0E5CB079 /* 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>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
21E89413E97A865028E9F8E7 /* libPods-example.a in Frameworks */, 92F3BF4D0A6D1113D359EFA3 /* 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 */,
9B670364245CE8751F827745 /* libPods-example.a */, 358AE48123A5C7B504741D4F /* 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 = (
521745100EAB4A9BA708C727 /* Pods-example.debug.xcconfig */, 9240166696F5B2EC6A36F688 /* Pods-example.debug.xcconfig */,
F54AF3C297616CFF0E5CB079 /* Pods-example.release.xcconfig */, 950B7AF3DD3B011B04438348 /* 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 = (
14A37E37235579F72338737B /* [CP] Check Pods Manifest.lock */, F073E18940A3F2D5B6D5B315 /* [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 */,
6F216721E9B6E9B3144F7620 /* [CP] Embed Pods Frameworks */, 9E691098E1BDE21450BE0492 /* [CP] Embed Pods Frameworks */,
42F37D61A8BF807876CD4F07 /* [CP] Copy Pods Resources */, 9554DF7B7A44E78B79E32173 /* [CP] Copy Pods Resources */,
); );
buildRules = ( buildRules = (
); );
...@@ -183,60 +183,60 @@ ...@@ -183,60 +183,60 @@
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";
}; };
14A37E37235579F72338737B /* [CP] Check Pods Manifest.lock */ = { 9554DF7B7A44E78B79E32173 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
); );
inputFileListPaths = ( inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-resources-${CONFIGURATION}-input-files.xcfilelist",
); );
inputPaths = ( name = "[CP] Copy Pods Resources";
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = ( outputFileListPaths = (
); "${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-resources-${CONFIGURATION}-output-files.xcfilelist",
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-example-checkManifestLockResult.txt",
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh; shellPath = /bin/sh;
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 = "\"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-resources.sh\"\n";
showEnvVarsInLog = 0; showEnvVarsInLog = 0;
}; };
42F37D61A8BF807876CD4F07 /* [CP] Copy Pods Resources */ = { 9E691098E1BDE21450BE0492 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
); );
inputFileListPaths = ( inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-resources-${CONFIGURATION}-input-files.xcfilelist", "${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-frameworks-${CONFIGURATION}-input-files.xcfilelist",
); );
name = "[CP] Copy Pods Resources"; name = "[CP] Embed Pods Frameworks";
outputFileListPaths = ( outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-resources-${CONFIGURATION}-output-files.xcfilelist", "${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-frameworks-${CONFIGURATION}-output-files.xcfilelist",
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh; shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-resources.sh\"\n"; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-frameworks.sh\"\n";
showEnvVarsInLog = 0; showEnvVarsInLog = 0;
}; };
6F216721E9B6E9B3144F7620 /* [CP] Embed Pods Frameworks */ = { F073E18940A3F2D5B6D5B315 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
); );
inputFileListPaths = ( inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-frameworks-${CONFIGURATION}-input-files.xcfilelist",
); );
name = "[CP] Embed Pods Frameworks"; inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = ( outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-frameworks-${CONFIGURATION}-output-files.xcfilelist", );
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-example-checkManifestLockResult.txt",
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh; shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-frameworks.sh\"\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;
}; };
/* End PBXShellScriptBuildPhase section */ /* End PBXShellScriptBuildPhase section */
...@@ -255,7 +255,7 @@ ...@@ -255,7 +255,7 @@
/* Begin XCBuildConfiguration section */ /* Begin XCBuildConfiguration section */
13B07F941A680F5B00A75B9A /* Debug */ = { 13B07F941A680F5B00A75B9A /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = 521745100EAB4A9BA708C727 /* Pods-example.debug.xcconfig */; baseConfigurationReference = 9240166696F5B2EC6A36F688 /* 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 = F54AF3C297616CFF0E5CB079 /* Pods-example.release.xcconfig */; baseConfigurationReference = 950B7AF3DD3B011B04438348 /* Pods-example.release.xcconfig */;
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
......
...@@ -8,32 +8,32 @@ ...@@ -8,32 +8,32 @@
<key>BinaryPath</key> <key>BinaryPath</key>
<string>QmsPluginFramework.framework/QmsPluginFramework</string> <string>QmsPluginFramework.framework/QmsPluginFramework</string>
<key>LibraryIdentifier</key> <key>LibraryIdentifier</key>
<string>ios-arm64_x86_64-simulator</string> <string>ios-arm64</string>
<key>LibraryPath</key> <key>LibraryPath</key>
<string>QmsPluginFramework.framework</string> <string>QmsPluginFramework.framework</string>
<key>SupportedArchitectures</key> <key>SupportedArchitectures</key>
<array> <array>
<string>arm64</string> <string>arm64</string>
<string>x86_64</string>
</array> </array>
<key>SupportedPlatform</key> <key>SupportedPlatform</key>
<string>ios</string> <string>ios</string>
<key>SupportedPlatformVariant</key>
<string>simulator</string>
</dict> </dict>
<dict> <dict>
<key>BinaryPath</key> <key>BinaryPath</key>
<string>QmsPluginFramework.framework/QmsPluginFramework</string> <string>QmsPluginFramework.framework/QmsPluginFramework</string>
<key>LibraryIdentifier</key> <key>LibraryIdentifier</key>
<string>ios-arm64</string> <string>ios-arm64_x86_64-simulator</string>
<key>LibraryPath</key> <key>LibraryPath</key>
<string>QmsPluginFramework.framework</string> <string>QmsPluginFramework.framework</string>
<key>SupportedArchitectures</key> <key>SupportedArchitectures</key>
<array> <array>
<string>arm64</string> <string>arm64</string>
<string>x86_64</string>
</array> </array>
<key>SupportedPlatform</key> <key>SupportedPlatform</key>
<string>ios</string> <string>ios</string>
<key>SupportedPlatformVariant</key>
<string>simulator</string>
</dict> </dict>
</array> </array>
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
......
...@@ -512,7 +512,7 @@ ...@@ -512,7 +512,7 @@
return; return;
} }
[APIClient fetchCancelAppointment:self.appointmentData[@"appointment_id"] [APIClient requestCancelAppointment:self.appointmentData[@"appointment_id"]
reason:remarks reason:remarks
completion:^(NSDictionary *data, NSError *error) completion:^(NSDictionary *data, NSError *error)
{ {
......
...@@ -285,7 +285,7 @@ ...@@ -285,7 +285,7 @@
- (void)viewDidLoad { - (void)viewDidLoad {
[super viewDidLoad]; [super viewDidLoad];
[self fetchAccessItem]; [self requestAccessItem];
self.view.backgroundColor = UIColor.whiteColor; self.view.backgroundColor = UIColor.whiteColor;
[self setupHeader]; [self setupHeader];
...@@ -361,7 +361,7 @@ ...@@ -361,7 +361,7 @@
__weak typeof(self) weakSelf = self; __weak typeof(self) weakSelf = self;
[self.tableView addPullToRefresh:^(UIRefreshControl *refresh) { [self.tableView addPullToRefresh:^(UIRefreshControl *refresh) {
[weakSelf fetchAccessItem]; [weakSelf requestAccessItem];
}]; }];
[self.view addSubview:self.tableView]; [self.view addSubview:self.tableView];
...@@ -375,8 +375,8 @@ ...@@ -375,8 +375,8 @@
} }
#pragma mark - Api #pragma mark - Api
- (void)fetchAccessItem{ - (void)requestAccessItem{
[APIClient fetchAccessItem:@"135" [APIClient requestAccessItem:@"135"
completion:^(BOOL success, NSDictionary *data, NSError *error) { completion:^(BOOL success, NSDictionary *data, NSError *error) {
NSLog(@"📦 Received General Info Data: %@", data); NSLog(@"📦 Received General Info Data: %@", data);
......
...@@ -43,7 +43,7 @@ ...@@ -43,7 +43,7 @@
[super viewDidLoad]; [super viewDidLoad];
self.view.backgroundColor = UIColor.whiteColor; self.view.backgroundColor = UIColor.whiteColor;
[self fetchGeneralInfo]; [self requestGeneralInfo];
[self setupNavBar]; [self setupNavBar];
[self setupUnitInfoHeader]; [self setupUnitInfoHeader];
[self setupTabsBar]; [self setupTabsBar];
...@@ -931,7 +931,7 @@ ...@@ -931,7 +931,7 @@
return; return;
} }
[APIClient fetchCancelAppointment:self.appointmentData[@"id"] [APIClient requestCancelAppointment:self.appointmentData[@"id"]
reason:remarks reason:remarks
completion:^(NSDictionary *data, NSError *error) completion:^(NSDictionary *data, NSError *error)
{ {
...@@ -965,8 +965,8 @@ ...@@ -965,8 +965,8 @@
}]; }];
} }
-(void) fetchGeneralInfo { -(void) requestGeneralInfo {
[APIClient fetchGeneralInfo:^(NSDictionary *data, NSError *error) { [APIClient requestGeneralInfo:^(NSDictionary *data, NSError *error) {
if (error) return; if (error) return;
NSDictionary *generalInfo = data[@"Data"]; NSDictionary *generalInfo = data[@"Data"];
self.floorPlanImage = generalInfo[@"image"]; self.floorPlanImage = generalInfo[@"image"];
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#import "CalendarView.h" #import "CalendarView.h"
#import "NSDate+CalendarHelpers.h" #import "NSDate+CalendarHelpers.h"
#import "AppointmentTimeSlotViewController.h" #import "AppointmentTimeSlotViewController.h"
#import "PullToRefresh.h"
@interface AppointmentViewController () <WKNavigationDelegate, WKUIDelegate, UIScrollViewDelegate, CalendarViewDelegate> @interface AppointmentViewController () <WKNavigationDelegate, WKUIDelegate, UIScrollViewDelegate, CalendarViewDelegate>
...@@ -58,23 +59,71 @@ ...@@ -58,23 +59,71 @@
@implementation AppointmentViewController @implementation AppointmentViewController
- (void)viewDidLoad { - (void)viewDidLoad {
[super viewDidLoad]; [super viewDidLoad];
self.view.backgroundColor = UIColor.whiteColor; self.view.backgroundColor = UIColor.whiteColor;
[self setupUI]; [self setupHeader];
[self setupTermsOverlay]; [self setupFooter];
[self fetchAppointment];
[self fetchGeneralInfo]; [self setupUI];
} }
#pragma mark - Setup UI #pragma mark - Setup UI
- (void)setupUI { - (void)setupUI {
[self setupHeader]; // Scroll view
[self setupLabels]; self.scrollView = [[UIScrollView alloc] init];
[self setupCalendarSection]; self.scrollView.translatesAutoresizingMaskIntoConstraints = NO;
[self setupFooter]; self.scrollView.alwaysBounceVertical = YES;
[self setupTutorialOverlay]; [self.view addSubview:self.scrollView];
self.contentView = [[UIView alloc] init];
self.contentView.translatesAutoresizingMaskIntoConstraints = NO;
[self.scrollView addSubview:self.contentView];
UILayoutGuide *safe = self.view.safeAreaLayoutGuide;
[NSLayoutConstraint activateConstraints:@[
[self.scrollView.topAnchor constraintEqualToAnchor:self.headerBar.bottomAnchor],
[self.scrollView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
[self.scrollView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
[self.scrollView.bottomAnchor constraintEqualToAnchor:self.footerView.topAnchor],
[self.contentView.topAnchor constraintEqualToAnchor:self.scrollView.topAnchor],
[self.contentView.leadingAnchor constraintEqualToAnchor:self.scrollView.leadingAnchor],
[self.contentView.trailingAnchor constraintEqualToAnchor:self.scrollView.trailingAnchor],
[self.contentView.bottomAnchor constraintEqualToAnchor:self.scrollView.bottomAnchor],
[self.contentView.widthAnchor constraintEqualToAnchor:self.scrollView.widthAnchor],
]];
// Create labels, calendar, legend
[self setupLabels:self.contentView];
[self setupCalendarSection:self.contentView];
// Optional: wrap in stack if you want
UIStackView *contentStack = [[UIStackView alloc] initWithArrangedSubviews:@[self.unitLabel, self.unitName, self.stepLabel, self.calendarContainer, self.legendStack]];
contentStack.axis = UILayoutConstraintAxisVertical;
contentStack.spacing = 16;
contentStack.translatesAutoresizingMaskIntoConstraints = NO;
[self.contentView addSubview:contentStack];
[NSLayoutConstraint activateConstraints:@[
[contentStack.topAnchor constraintEqualToAnchor:self.contentView.topAnchor constant:16],
[contentStack.leadingAnchor constraintEqualToAnchor:self.contentView.leadingAnchor constant:16],
[contentStack.trailingAnchor constraintEqualToAnchor:self.contentView.trailingAnchor constant:-16],
[contentStack.bottomAnchor constraintEqualToAnchor:self.contentView.bottomAnchor constant:-16]
]];
// Tutorial overlay added last so it’s on top
[self setupTutorialOverlay];
[self setupTermsOverlay];
// Pull-to-refresh
__weak typeof(self) weakSelf = self;
[self.scrollView addPullToRefresh:^(UIRefreshControl *refreshControl) {
[weakSelf refreshAppointmentData:refreshControl];
}];
} }
#pragma mark - Navigation Bar #pragma mark - Navigation Bar
- (void)setupHeader { - (void)setupHeader {
...@@ -154,7 +203,11 @@ ...@@ -154,7 +203,11 @@
self.termsWebView.scrollView.delegate = self; self.termsWebView.scrollView.delegate = self;
self.termsWebView.navigationDelegate = self; self.termsWebView.navigationDelegate = self;
[self.termsContainer addSubview:self.termsWebView]; [self.termsContainer addSubview:self.termsWebView];
self.termsContainer.backgroundColor = [UIColor yellowColor];
self.termsWebView.backgroundColor = [UIColor redColor];
// Agree button // Agree button
self.agreeButton = [UIButton buttonWithType:UIButtonTypeSystem]; self.agreeButton = [UIButton buttonWithType:UIButtonTypeSystem];
self.agreeButton.translatesAutoresizingMaskIntoConstraints = NO; self.agreeButton.translatesAutoresizingMaskIntoConstraints = NO;
...@@ -192,22 +245,20 @@ ...@@ -192,22 +245,20 @@
#pragma mark - Labels #pragma mark - Labels
- (void)setupLabels { - (void)setupLabels:(UIView *)parent {
UILayoutGuide *safe = self.view.safeAreaLayoutGuide;
self.unitLabel = [[UILabel alloc] init]; self.unitLabel = [[UILabel alloc] init];
self.unitLabel.text = @"For Unit No.:"; self.unitLabel.text = @"For Unit No.:";
self.unitLabel.font = [UIFont systemFontOfSize:16 weight:UIFontWeightThin]; self.unitLabel.font = [UIFont systemFontOfSize:16 weight:UIFontWeightThin];
self.unitLabel.textColor = [UIColor grayColor]; self.unitLabel.textColor = [UIColor grayColor];
self.unitLabel.translatesAutoresizingMaskIntoConstraints = NO; self.unitLabel.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:self.unitLabel]; [parent addSubview:self.unitLabel];
self.unitName = [[UILabel alloc] init]; self.unitName = [[UILabel alloc] init];
self.unitName.text = [NSString stringWithFormat:@"%@, %@", self.projectName, self.projectCode]; self.unitName.text = [NSString stringWithFormat:@"%@, %@", self.projectName, self.projectCode];
self.unitName.font = [UIFont systemFontOfSize:16 weight:UIFontWeightMedium]; self.unitName.font = [UIFont systemFontOfSize:16 weight:UIFontWeightMedium];
self.unitName.textColor = [UIColor blackColor]; self.unitName.textColor = [UIColor blackColor];
self.unitName.translatesAutoresizingMaskIntoConstraints = NO; self.unitName.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:self.unitName]; [parent addSubview:self.unitName];
self.stepLabel = [[UILabel alloc] init]; self.stepLabel = [[UILabel alloc] init];
self.stepLabel.text = @"Step 1/3: Select your preferred date"; self.stepLabel.text = @"Step 1/3: Select your preferred date";
...@@ -218,31 +269,31 @@ ...@@ -218,31 +269,31 @@
self.stepLabel.backgroundColor = [UIColor colorWithWhite:0.95 alpha:1.0]; self.stepLabel.backgroundColor = [UIColor colorWithWhite:0.95 alpha:1.0];
self.stepLabel.layer.cornerRadius = 6.0; self.stepLabel.layer.cornerRadius = 6.0;
self.stepLabel.clipsToBounds = YES; self.stepLabel.clipsToBounds = YES;
[self.view addSubview:self.stepLabel]; [parent addSubview:self.stepLabel];
[NSLayoutConstraint activateConstraints:@[ [NSLayoutConstraint activateConstraints:@[
[self.unitLabel.topAnchor constraintEqualToAnchor:self.headerBar.bottomAnchor constant:16], [self.unitLabel.topAnchor constraintEqualToAnchor:parent.topAnchor constant:16],
[self.unitLabel.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:16], [self.unitLabel.leadingAnchor constraintEqualToAnchor:parent.leadingAnchor constant:16],
[self.unitLabel.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:-16], [self.unitLabel.trailingAnchor constraintEqualToAnchor:parent.trailingAnchor constant:-16],
[self.unitName.topAnchor constraintEqualToAnchor:self.unitLabel.bottomAnchor constant:8], [self.unitName.topAnchor constraintEqualToAnchor:self.unitLabel.bottomAnchor constant:8],
[self.unitName.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:16], [self.unitName.leadingAnchor constraintEqualToAnchor:parent.leadingAnchor constant:16],
[self.unitName.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:-16], [self.unitName.trailingAnchor constraintEqualToAnchor:parent.trailingAnchor constant:-16],
[self.stepLabel.topAnchor constraintEqualToAnchor:self.unitName.bottomAnchor constant:16], [self.stepLabel.topAnchor constraintEqualToAnchor:self.unitName.bottomAnchor constant:16],
[self.stepLabel.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:16], [self.stepLabel.leadingAnchor constraintEqualToAnchor:parent.leadingAnchor constant:16],
[self.stepLabel.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:-16], [self.stepLabel.trailingAnchor constraintEqualToAnchor:parent.trailingAnchor constant:-16],
[self.stepLabel.heightAnchor constraintEqualToConstant:30] [self.stepLabel.heightAnchor constraintEqualToConstant:30]
]]; ]];
} }
#pragma mark - Calendar Section #pragma mark - Calendar Section
- (void)setupCalendarSection { - (void)setupCalendarSection:(UIView *)parent {
self.calendarContainer = [[UIView alloc] init]; self.calendarContainer = [[UIView alloc] init];
self.calendarContainer.backgroundColor = [UIColor whiteColor]; self.calendarContainer.backgroundColor = [UIColor whiteColor];
self.calendarContainer.layer.cornerRadius = 12.0; self.calendarContainer.layer.cornerRadius = 12.0;
self.calendarContainer.translatesAutoresizingMaskIntoConstraints = NO; self.calendarContainer.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:self.calendarContainer]; [parent addSubview:self.calendarContainer];
self.calendarView = [[CalendarView alloc] initWithFrame:CGRectZero]; self.calendarView = [[CalendarView alloc] initWithFrame:CGRectZero];
self.calendarView.translatesAutoresizingMaskIntoConstraints = NO; self.calendarView.translatesAutoresizingMaskIntoConstraints = NO;
...@@ -256,7 +307,7 @@ ...@@ -256,7 +307,7 @@
self.legendStack.alignment = UIStackViewAlignmentCenter; self.legendStack.alignment = UIStackViewAlignmentCenter;
self.legendStack.distribution = UIStackViewDistributionEqualSpacing; self.legendStack.distribution = UIStackViewDistributionEqualSpacing;
self.legendStack.translatesAutoresizingMaskIntoConstraints = NO; self.legendStack.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:self.legendStack]; [parent addSubview:self.legendStack];
NSArray *legendData = @[ NSArray *legendData = @[
@{@"title": @"Available", @"color": UIColor.systemGreenColor}, @{@"title": @"Available", @"color": UIColor.systemGreenColor},
...@@ -285,13 +336,13 @@ ...@@ -285,13 +336,13 @@
CGFloat w = UIScreen.mainScreen.bounds.size.width - 16 - 16 - 12 - 12; // container + inner padding CGFloat w = UIScreen.mainScreen.bounds.size.width - 16 - 16 - 12 - 12; // container + inner padding
CGFloat cell = floor(w / 7.0); CGFloat cell = floor(w / 7.0);
CGFloat rows = 6; // max rows calendar ever needs CGFloat rows = 6; // max rows
CGFloat calendarHeight = 40 + 28 + (cell * 0.9 + 12) * rows + 12 + 20; CGFloat calendarHeight = 40 + 28 + (cell * 0.9 + 12) * rows + 12 + 20;
[NSLayoutConstraint activateConstraints:@[ [NSLayoutConstraint activateConstraints:@[
[self.calendarContainer.topAnchor constraintEqualToAnchor:self.stepLabel.bottomAnchor constant:16], [self.calendarContainer.topAnchor constraintEqualToAnchor:self.stepLabel.bottomAnchor constant:16],
[self.calendarContainer.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:16], [self.calendarContainer.leadingAnchor constraintEqualToAnchor:parent.leadingAnchor constant:16],
[self.calendarContainer.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:-16], [self.calendarContainer.trailingAnchor constraintEqualToAnchor:parent.trailingAnchor constant:-16],
[self.calendarContainer.heightAnchor constraintEqualToConstant:calendarHeight], [self.calendarContainer.heightAnchor constraintEqualToConstant:calendarHeight],
[self.calendarView.topAnchor constraintEqualToAnchor:self.calendarContainer.topAnchor constant:12], [self.calendarView.topAnchor constraintEqualToAnchor:self.calendarContainer.topAnchor constant:12],
...@@ -300,7 +351,7 @@ ...@@ -300,7 +351,7 @@
[self.calendarView.bottomAnchor constraintEqualToAnchor:self.calendarContainer.bottomAnchor constant:-12], [self.calendarView.bottomAnchor constraintEqualToAnchor:self.calendarContainer.bottomAnchor constant:-12],
[self.legendStack.topAnchor constraintEqualToAnchor:self.calendarContainer.bottomAnchor constant:16], [self.legendStack.topAnchor constraintEqualToAnchor:self.calendarContainer.bottomAnchor constant:16],
[self.legendStack.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor], [self.legendStack.centerXAnchor constraintEqualToAnchor:parent.centerXAnchor],
]]; ]];
} }
...@@ -398,8 +449,8 @@ ...@@ -398,8 +449,8 @@
} }
#pragma mark - api #pragma mark - api
- (void)fetchAppointment { - (void)requestAppointment {
[APIClient fetchAppointment:^(NSDictionary *data, NSError *error) { [APIClient requestAppointment:^(NSDictionary *data, NSError *error) {
if (error) return; if (error) return;
if (!data[@"Data"]) return; if (!data[@"Data"]) return;
...@@ -430,15 +481,17 @@ ...@@ -430,15 +481,17 @@
NSString *termsHTML = selectedTypeDict[@"terms"] ?: @""; NSString *termsHTML = selectedTypeDict[@"terms"] ?: @"";
NSString *styledHTML = [NSString stringWithFormat: NSString *styledHTML = [NSString stringWithFormat:
@"<html>" @"<html>"
"<head>" "<head>"
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1.0\">" "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1.0\">"
"<style>" "<style>"
"body { font-size: 14px; font-family: -apple-system; line-height: 1.5; padding: 12px; background-color: #F9F9F9; }" "body { font-size: 14px; font-family: -apple-system; line-height: 1.5; padding: 12px; background-color: #F9F9F9; color: #000000; }"
"p { margin-bottom: 12px; }" "p, div, span, li { color: #000000 !important; }"
"</style>" "p { margin-bottom: 12px; }"
"</head>" "</style>"
"<body>%@</body>" "</head>"
"</html>", termsHTML]; "<body>%@</body>"
"</html>", termsHTML];
self.termsAndCondition = styledHTML; self.termsAndCondition = styledHTML;
...@@ -450,8 +503,8 @@ ...@@ -450,8 +503,8 @@
}]; }];
} }
-(void) fetchGeneralInfo { -(void) requestGeneralInfo {
[APIClient fetchGeneralInfo:^(NSDictionary *data, NSError *error) { [APIClient requestGeneralInfo:^(NSDictionary *data, NSError *error) {
if (error) return; if (error) return;
NSDictionary *generalInfo = data[@"Data"]; NSDictionary *generalInfo = data[@"Data"];
...@@ -575,10 +628,16 @@ ...@@ -575,10 +628,16 @@
- (void)showTermsOverlay { - (void)showTermsOverlay {
self.termsOverlay.hidden = NO; self.termsOverlay.hidden = NO;
[self.view bringSubviewToFront:self.termsOverlay];
self.termsOverlay.alpha = 0.0; self.termsOverlay.alpha = 0.0;
[UIView animateWithDuration:0.25 animations:^{ [UIView animateWithDuration:0.25 animations:^{
self.termsOverlay.alpha = 1.0; self.termsOverlay.alpha = 1.0;
} completion:^(BOOL finished) {
// Ensure webview loads now
if (self.termsAndCondition) {
[self.termsWebView loadHTMLString:self.termsAndCondition baseURL:nil];
}
}]; }];
} }
...@@ -642,5 +701,44 @@ ...@@ -642,5 +701,44 @@
[self presentViewController:alert animated:YES completion:nil]; [self presentViewController:alert animated:YES completion:nil];
} }
- (void)refreshAppointmentData:(UIRefreshControl *)refreshControl {
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
[APIClient requestAppointment:^(NSDictionary *data, NSError *error) {
if (data[@"Data"]) {
NSDictionary *dataDict = data[@"Data"];
self.appointmentData = dataDict;
NSString *termsHTML = dataDict[self.appointmentType][@"terms"] ?: @"";
NSString *styledHTML = [NSString stringWithFormat:
@"<html><head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">"
"<style>body { font-size:14px; font-family:-apple-system; padding:12px; }</style>"
"</head><body>%@</body></html>", termsHTML];
self.termsAndCondition = styledHTML;
dispatch_async(dispatch_get_main_queue(), ^{
[self.termsWebView loadHTMLString:styledHTML baseURL:nil];
});
NSArray *slots = dataDict[self.appointmentType][@"slots"] ?: @[];
self.allSlots = slots;
[self buildMarkedDatesFromSlots:slots];
[self.calendarView.collectionView reloadData];
}
dispatch_group_leave(group);
}];
dispatch_group_enter(group);
[APIClient requestGeneralInfo:^(NSDictionary *data, NSError *error) {
id reps = data[@"Data"][@"representative"];
self.representativeList = [reps isKindOfClass:[NSArray class]] ? reps : @[];
dispatch_group_leave(group);
}];
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
[self.scrollView endPullToRefresh];
});
}
@end @end
...@@ -138,7 +138,7 @@ ...@@ -138,7 +138,7 @@
[self setupListingView]; [self setupListingView];
[self setupFilterOverlay]; [self setupFilterOverlay];
[self setupLoadingOverlay]; [self setupLoadingOverlay];
[self fetchDashboardInfo]; [self requestDashboardInfo];
} }
#pragma mark - Top Bar #pragma mark - Top Bar
...@@ -679,7 +679,7 @@ ...@@ -679,7 +679,7 @@
} }
- (void)onRefresh { - (void)onRefresh {
[self fetchDashboardInfo]; [self requestDashboardInfo];
} }
#pragma mark - Bottom Button #pragma mark - Bottom Button
...@@ -924,10 +924,10 @@ ...@@ -924,10 +924,10 @@
} }
#pragma mark - api #pragma mark - api
- (void)fetchDashboardInfo { - (void)requestDashboardInfo {
[self.loadingView startAnimating]; [self.loadingView startAnimating];
[APIClient fetchDashboardInfo:^(NSDictionary *data, NSError *error) { [APIClient requestDashboardInfo:^(NSDictionary *data, NSError *error) {
[self.loadingView stopAnimating]; [self.loadingView stopAnimating];
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#import "GeneralInfoViewController.h" #import "GeneralInfoViewController.h"
#import "AccessItemViewController.h" #import "AccessItemViewController.h"
#import "OfflineSyncManager.h" #import "OfflineSyncManager.h"
#import "LocalStorage.h"
@interface DashboardViewController () @interface DashboardViewController ()
...@@ -81,7 +82,6 @@ ...@@ -81,7 +82,6 @@
NSLog(@"dashboard.drawerController = %@", self.drawerController); NSLog(@"dashboard.drawerController = %@", self.drawerController);
// [[OfflineSyncManager shared] clearOfflineQueue]; // [[OfflineSyncManager shared] clearOfflineQueue];
} }
- (void)viewWillAppear:(BOOL)animated { - (void)viewWillAppear:(BOOL)animated {
...@@ -113,13 +113,13 @@ ...@@ -113,13 +113,13 @@
__block NSDictionary *announcementData = nil; __block NSDictionary *announcementData = nil;
dispatch_group_enter(group); dispatch_group_enter(group);
[APIClient fetchDashboardInfo:^(NSDictionary *data, NSError *error) { [APIClient requestDashboardInfo:^(NSDictionary *data, NSError *error) {
if (!error) dashboardData = data; if (!error) dashboardData = data;
dispatch_group_leave(group); dispatch_group_leave(group);
}]; }];
dispatch_group_enter(group); dispatch_group_enter(group);
[APIClient fetchAnnouncement:^(NSDictionary *data, NSError *error) { [APIClient requestAnnouncement:^(NSDictionary *data, NSError *error) {
if (!error) announcementData = data; if (!error) announcementData = data;
dispatch_group_leave(group); dispatch_group_leave(group);
}]; }];
...@@ -593,7 +593,7 @@ ...@@ -593,7 +593,7 @@
NSDictionary *appData = data[@"AppData"]; NSDictionary *appData = data[@"AppData"];
if (![appData[@"status"] isEqualToString:@"success"]) { if (![appData[@"status"] isEqualToString:@"success"]) {
NSLog(@"Announcement fetch failed: %@", appData[@"message"]); NSLog(@"Announcement request failed: %@", appData[@"message"]);
return; return;
} }
......
...@@ -83,7 +83,7 @@ typedef struct { ...@@ -83,7 +83,7 @@ typedef struct {
self.documentsContainer.clipsToBounds = YES; self.documentsContainer.clipsToBounds = YES;
self.formsContainer.clipsToBounds = YES; self.formsContainer.clipsToBounds = YES;
[self fetchGeneralInfo]; [self requestGeneralInfo];
[self setupTopBar]; [self setupTopBar];
[self setupTabControl]; [self setupTabControl];
[self setupScrollView]; [self setupScrollView];
...@@ -540,8 +540,8 @@ typedef struct { ...@@ -540,8 +540,8 @@ typedef struct {
} }
#pragma mark - api #pragma mark - api
- (void)fetchGeneralInfo { - (void)requestGeneralInfo {
[APIClient fetchGeneralInfo:^(NSDictionary *data, NSError *error) { [APIClient requestGeneralInfo:^(NSDictionary *data, NSError *error) {
if (error || !data) { if (error || !data) {
NSLog(@"❌ General info error: %@", error.localizedDescription); NSLog(@"❌ General info error: %@", error.localizedDescription);
return; return;
......
...@@ -67,11 +67,11 @@ ...@@ -67,11 +67,11 @@
} }
- (void)handleGetUnitPlan { - (void)handleGetUnitPlan {
NSLog(@"🌐 Fetching Unit Plan data..."); NSLog(@"🌐 requesting Unit Plan data...");
[APIClient fetchGetUnitPlan:^(NSDictionary *data, NSError *error) { [APIClient requestGetUnitPlan:^(NSDictionary *data, NSError *error) {
if (error) { if (error) {
NSLog(@"❌ Unit Plan fetch failed: %@", error.localizedDescription); NSLog(@"❌ Unit Plan request failed: %@", error.localizedDescription);
return; return;
} }
...@@ -165,7 +165,6 @@ ...@@ -165,7 +165,6 @@
}]; }];
} }
- (void)highlightExistingIssueAtCoordinates:(CGFloat)x Y:(CGFloat)y { - (void)highlightExistingIssueAtCoordinates:(CGFloat)x Y:(CGFloat)y {
NSLog(@"📍 Highlighting existing issue at (%.2f, %.2f)", x, y); NSLog(@"📍 Highlighting existing issue at (%.2f, %.2f)", x, y);
...@@ -702,20 +701,19 @@ ...@@ -702,20 +701,19 @@
} }
if (self.locationID) { if (self.locationID) {
NSLog(@"📡 Fetching settings for location %@ and plan %@", self.locationID, self.planID); NSLog(@"📡 requesting settings for location %@ and plan %@", self.locationID, self.planID);
[self handleSettingsByLocation:self.locationID planID:self.planID]; [self handleSettingsByLocation:self.locationID planID:self.planID];
} else { } else {
NSLog(@"⚠️ No match found for %@", self.selectedLocation); NSLog(@"⚠️ No match found for %@", self.selectedLocation);
} }
} }
- (void)handleSettingsByLocation:(NSString *)locationID planID:(NSString *)planID { - (void)handleSettingsByLocation:(NSString *)locationID planID:(NSString *)planID {
[APIClient fetchSettingsByLocation:locationID [APIClient requestSettingsByLocation:locationID
projectID:@"8" projectID:@"8"
completion:^(NSDictionary *data, NSError *error) { completion:^(NSDictionary *data, NSError *error) {
if (error) { if (error) {
NSLog(@"❌ Error fetching settings: %@", error); NSLog(@"❌ Error requesting settings: %@", error);
return; return;
} }
......
...@@ -126,10 +126,10 @@ ...@@ -126,10 +126,10 @@
#pragma mark - API request/response #pragma mark - API request/response
- (void)handleIssues { - (void)handleIssues {
[APIClient fetchHistory:self.issueID [APIClient requestHistory:self.issueID
completion:^(NSDictionary *data, NSError *error) { completion:^(NSDictionary *data, NSError *error) {
if (error) { if (error) {
NSLog(@"❌ Error fetching history: %@", error); NSLog(@"❌ Error requesting history: %@", error);
return; return;
} }
......
...@@ -298,11 +298,11 @@ ...@@ -298,11 +298,11 @@
#pragma mark - Networking #pragma mark - Networking
- (void)handleGetIssue { - (void)handleGetIssue {
NSLog(@"🌐 Fetching Issue data..."); NSLog(@"🌐 requesting Issue data...");
[APIClient fetchIssues:^(NSDictionary *data, NSError *error) { [APIClient requestIssues:^(NSDictionary *data, NSError *error) {
if (error) { if (error) {
NSLog(@"❌ Issue fetch failed: %@", error.localizedDescription); NSLog(@"❌ Issue request failed: %@", error.localizedDescription);
return; return;
} }
......
...@@ -174,7 +174,7 @@ ...@@ -174,7 +174,7 @@
[self setupSearchBar]; [self setupSearchBar];
[self setupTableView]; [self setupTableView];
[self setupBottomBar]; [self setupBottomBar];
[self fetchIssues]; [self requestIssues];
} }
- (void)viewWillAppear:(BOOL)animated { - (void)viewWillAppear:(BOOL)animated {
...@@ -382,8 +382,8 @@ ...@@ -382,8 +382,8 @@
} }
#pragma mark - Data #pragma mark - Data
- (void)fetchIssues { - (void)requestIssues {
[APIClient fetchIssues:^(NSDictionary *data, NSError *error) { [APIClient requestIssues:^(NSDictionary *data, NSError *error) {
if (error) return; if (error) return;
NSArray *allIssues = data[@"Data"] ?: @[]; NSArray *allIssues = data[@"Data"] ?: @[];
......
...@@ -264,7 +264,7 @@ ...@@ -264,7 +264,7 @@
- (void)requestDeleteIssue:(NSString *)issueID remarks:(NSString *)remarks { - (void)requestDeleteIssue:(NSString *)issueID remarks:(NSString *)remarks {
NSLog(@"🚀 Sending DELETE ISSUE for ID=%@, remarks=%@", issueID, remarks); NSLog(@"🚀 Sending DELETE ISSUE for ID=%@, remarks=%@", issueID, remarks);
[APIClient fetchDeleteIssue:issueID remarks:remarks completion:^(NSDictionary *data, NSError *error) { [APIClient requestDeleteIssue:issueID remarks:remarks completion:^(NSDictionary *data, NSError *error) {
if (error) { if (error) {
NSLog(@"Error %@", error.localizedDescription); NSLog(@"Error %@", error.localizedDescription);
return; return;
...@@ -301,7 +301,7 @@ ...@@ -301,7 +301,7 @@
-(void)requestVoidIssue:(NSArray *)ids remarks:(NSString *)remarks { -(void)requestVoidIssue:(NSArray *)ids remarks:(NSString *)remarks {
NSLog(@"🚀 Sending VOID ISSUE for ID=%@, remarks=%@", self.selectedIssues, remarks); NSLog(@"🚀 Sending VOID ISSUE for ID=%@, remarks=%@", self.selectedIssues, remarks);
[APIClient fetchVoidIssue:ids [APIClient requestVoidIssue:ids
remarks:remarks remarks:remarks
completion:^(NSDictionary *data, NSError *error) { completion:^(NSDictionary *data, NSError *error) {
if (error) { if (error) {
......
//// SyncEngine.h
//
//#import <Foundation/Foundation.h>
//@interface SyncEngine : NSObject
//
//@property (nonatomic, assign) BOOL submitPending;
//@property (nonatomic, assign) BOOL downloadUnits;
//
//- (void)startWithProgress:(SyncProgressBlock)progress
// completion:(SyncCompletionBlock)completion;
//
//@end
//
//// SyncEngine.mm
//
//#import "SyncEngine.h"
//#import "APIClient.h"
//#import "NetworkManager.h"
//
//@interface SyncEngine ()
//
//// Callbacks
//@property (nonatomic, copy) SyncProgressBlock progressBlock;
//@property (nonatomic, copy) SyncCompletionBlock completionBlock;
//
//// Progress tracking
//@property (nonatomic, assign) NSInteger currentStep;
//@property (nonatomic, assign) NSInteger totalSteps;
//
//@end
//
//@implementation SyncEngine
//
//#pragma mark - Public Entry Point
//
//- (void)startWithProgress:(SyncProgressBlock)progress
// completion:(SyncCompletionBlock)completion {
//
// self.progressBlock = progress;
// self.completionBlock = completion;
//
// self.currentStep = 0;
// self.totalSteps = [self calculateTotalSteps];
//
// [self stepCheckNetwork];
//}
//
//#pragma mark - Step Count
//
//- (NSInteger)calculateTotalSteps {
// NSInteger steps = 1; // check network
//
// if (self.submitPending) {
// steps += 1; // submit pending
// }
//
// if (self.downloadUnits) {
// steps += 1; // download data
// }
//
// steps += 1; // finalize
//
// return steps;
//}
//
//#pragma mark - Step 1: Network Check
//
//- (void)stepCheckNetwork {
// [self reportProgress:@"Checking network"];
//
// if (![NetworkManager.sharedManager isConnected]) {
// NSError *error =
// [NSError errorWithDomain:@"Sync"
// code:-1009
// userInfo:@{
// NSLocalizedDescriptionKey :
// @"No internet connection"
// }];
// [self finish:NO error:error];
// return;
// }
//
// if (self.submitPending) {
// [self stepSubmitPending];
// } else if (self.downloadUnits) {
// [self stepDownloadCommonAreas];
// } else {
// [self stepFinalize];
// }
//}
//
//#pragma mark - Step 2: Submit Pending Offline Items
//
//- (void)stepSubmitPending {
// [self reportProgress:@"Submitting pending items"];
//
// __weak typeof(self) weakSelf = self;
//
// [self submitPendingItemsWithCompletion:^(BOOL success, NSError *error) {
// __strong typeof(weakSelf) self = weakSelf;
//
// if (!success) {
// [self finish:NO error:error];
// return;
// }
//
// if (self.downloadUnits) {
// [self stepDownloadCommonAreas];
// } else {
// [self stepFinalize];
// }
// }];
//}
//
//#pragma mark - Step 3: Download Data
//
//- (void)stepDownloadCommonAreas {
// [self reportProgress:@"Downloading common areas"];
//
// __weak typeof(self) weakSelf = self;
//
// [APIClient fetchCommonArea:^(NSDictionary *data, NSError *error) {
// __strong typeof(weakSelf) self = weakSelf;
//
// if (error) {
// [self finish:NO error:error];
// return;
// }
//
// [self persistCommonAreas:data];
// [self stepFinalize];
// }];
//}
//
//#pragma mark - Step 4: Finalize
//
//- (void)stepFinalize {
// [self reportProgress:@"Finalizing sync"];
// [self finalizeSync];
// [self finish:YES error:nil];
//}
//
//#pragma mark - Progress & Completion Helpers
//
//- (void)reportProgress:(NSString *)message {
// self.currentStep++;
//
// if (self.progressBlock) {
// self.progressBlock(self.currentStep,
// self.totalSteps,
// message);
// }
//}
//
//- (void)finish:(BOOL)success error:(NSError *)error {
// if (self.completionBlock) {
// self.completionBlock(success, error);
// }
//
// // Clean up references
// self.progressBlock = nil;
// self.completionBlock = nil;
//}
//
//#pragma mark - Internal Hooks (Implement These)
//
//- (void)submitPendingItemsWithCompletion:(void (^)(BOOL success, NSError *error))completion {
// // TODO:
// // - Query offline table
// // - Upload ONE item at a time
// // - Update local DB on success/failure
// // - Recursively continue until empty
// // - Call completion when done
//}
//
//- (void)persistCommonAreas:(NSDictionary *)data {
// // TODO:
// // Save common areas into local storage / DB
//}
//
//- (void)finalizeSync {
// // TODO:
// // - Update last sync timestamp
// // - Persist sync success state
//}
//
//@end
// SyncHistoryViewController.mm // SyncHistoryViewController.mm
#import "SyncHistoryViewController.h" #import "SyncHistoryViewController.h"
#import "OfflineSyncManager.h"
typedef NS_ENUM(NSInteger, HistorySyncStatus) { typedef NS_ENUM(NSInteger, HistorySyncStatus) {
HistorySyncStatusSuccess, HistorySyncStatusSuccess,
...@@ -14,6 +15,7 @@ typedef NS_ENUM(NSInteger, HistorySyncStatus) { ...@@ -14,6 +15,7 @@ typedef NS_ENUM(NSInteger, HistorySyncStatus) {
@property (nonatomic, strong) NSString *uploadedOn; @property (nonatomic, strong) NSString *uploadedOn;
@property (nonatomic, strong) NSString *actionMade; @property (nonatomic, strong) NSString *actionMade;
@property (nonatomic, assign) HistorySyncStatus status; @property (nonatomic, assign) HistorySyncStatus status;
@property (nonatomic, strong) NSDictionary *raw;
@end @end
@implementation HistorySyncItem @implementation HistorySyncItem
...@@ -139,7 +141,6 @@ typedef NS_ENUM(NSInteger, HistorySyncStatus) { ...@@ -139,7 +141,6 @@ typedef NS_ENUM(NSInteger, HistorySyncStatus) {
@property (nonatomic, strong) NSArray<HistorySyncItem *> *filteredItems; @property (nonatomic, strong) NSArray<HistorySyncItem *> *filteredItems;
@property (nonatomic, assign) NSInteger selectedTab; @property (nonatomic, assign) NSInteger selectedTab;
@end @end
@implementation SyncHistoryViewController @implementation SyncHistoryViewController
...@@ -157,7 +158,13 @@ typedef NS_ENUM(NSInteger, HistorySyncStatus) { ...@@ -157,7 +158,13 @@ typedef NS_ENUM(NSInteger, HistorySyncStatus) {
[self setupEmptyState]; [self setupEmptyState];
[self setupBottomButton]; [self setupBottomButton];
[self loadMockData]; [self loadHistoryData];
[self applyFilter];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[self loadHistoryData];
[self applyFilter]; [self applyFilter];
} }
...@@ -185,18 +192,18 @@ typedef NS_ENUM(NSInteger, HistorySyncStatus) { ...@@ -185,18 +192,18 @@ typedef NS_ENUM(NSInteger, HistorySyncStatus) {
self.titleLabel.translatesAutoresizingMaskIntoConstraints = NO; self.titleLabel.translatesAutoresizingMaskIntoConstraints = NO;
[self.headerView addSubview:self.titleLabel]; [self.headerView addSubview:self.titleLabel];
self.shareButton = [UIButton buttonWithType:UIButtonTypeSystem]; // self.shareButton = [UIButton buttonWithType:UIButtonTypeSystem];
[self.shareButton setTitle:@"Share to Email" forState:UIControlStateNormal]; // [self.shareButton setTitle:@"Share to Email" forState:UIControlStateNormal];
[self.shareButton setTitleColor:UIColor.whiteColor forState:UIControlStateNormal]; // [self.shareButton setTitleColor:UIColor.whiteColor forState:UIControlStateNormal];
self.shareButton.layer.borderColor = UIColor.whiteColor.CGColor; // self.shareButton.layer.borderColor = UIColor.whiteColor.CGColor;
self.shareButton.layer.borderWidth = 0.5; // self.shareButton.layer.borderWidth = 0.5;
self.shareButton.layer.cornerRadius = 14; // self.shareButton.layer.cornerRadius = 14;
[self.shareButton addTarget:self // [self.shareButton addTarget:self
action:@selector(onShare) // action:@selector(onShare)
forControlEvents:UIControlEventTouchUpInside]; // forControlEvents:UIControlEventTouchUpInside];
self.shareButton.titleLabel.font = [UIFont systemFontOfSize:14]; // self.shareButton.titleLabel.font = [UIFont systemFontOfSize:14];
self.shareButton.translatesAutoresizingMaskIntoConstraints = NO; // self.shareButton.translatesAutoresizingMaskIntoConstraints = NO;
[self.headerView addSubview:self.shareButton]; // [self.headerView addSubview:self.shareButton];
[NSLayoutConstraint activateConstraints:@[ [NSLayoutConstraint activateConstraints:@[
[self.headerView.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor], [self.headerView.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor],
...@@ -212,10 +219,10 @@ typedef NS_ENUM(NSInteger, HistorySyncStatus) { ...@@ -212,10 +219,10 @@ typedef NS_ENUM(NSInteger, HistorySyncStatus) {
[self.titleLabel.centerXAnchor constraintEqualToAnchor:self.headerView.centerXAnchor], [self.titleLabel.centerXAnchor constraintEqualToAnchor:self.headerView.centerXAnchor],
[self.titleLabel.centerYAnchor constraintEqualToAnchor:self.headerView.centerYAnchor], [self.titleLabel.centerYAnchor constraintEqualToAnchor:self.headerView.centerYAnchor],
[self.shareButton.trailingAnchor constraintEqualToAnchor:self.headerView.trailingAnchor constant:-8], // [self.shareButton.trailingAnchor constraintEqualToAnchor:self.headerView.trailingAnchor constant:-8],
[self.shareButton.centerYAnchor constraintEqualToAnchor:self.headerView.centerYAnchor], // [self.shareButton.centerYAnchor constraintEqualToAnchor:self.headerView.centerYAnchor],
[self.shareButton.heightAnchor constraintEqualToConstant:28], // [self.shareButton.heightAnchor constraintEqualToConstant:28],
[self.shareButton.widthAnchor constraintEqualToConstant:120], // [self.shareButton.widthAnchor constraintEqualToConstant:120],
]]; ]];
} }
...@@ -312,6 +319,43 @@ typedef NS_ENUM(NSInteger, HistorySyncStatus) { ...@@ -312,6 +319,43 @@ typedef NS_ENUM(NSInteger, HistorySyncStatus) {
]]; ]];
} }
#pragma mark - Data
- (void)loadHistoryData {
NSArray *sections =
[[OfflineSyncManager shared] historyItemsForUI];
[self dumpHistoryToFile:sections];
NSMutableArray *items = [NSMutableArray array];
for (NSDictionary *section in sections) {
NSArray *rows = section[@"items"];
for (NSDictionary *row in rows) {
HistorySyncItem *item = [HistorySyncItem new];
item.unitName = row[@"unitName"];
item.createdBy = row[@"createdBy"];
item.createdOn = row[@"createdOn"];
item.uploadedOn = row[@"uploadedOn"];
item.actionMade = row[@"type"];
NSString *status = row[@"status"];
item.status =
[status isEqualToString:@"FAILED"]
? HistorySyncStatusFailed
: HistorySyncStatusSuccess;
item.raw = row;
[items addObject:item];
}
}
self.allItems = items;
}
// Dummy data // Dummy data
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.filteredItems.count; return self.filteredItems.count;
...@@ -383,6 +427,10 @@ typedef NS_ENUM(NSInteger, HistorySyncStatus) { ...@@ -383,6 +427,10 @@ typedef NS_ENUM(NSInteger, HistorySyncStatus) {
[self.syncAgainButton setTitle:@"SYNC AGAIN" forState:UIControlStateNormal]; [self.syncAgainButton setTitle:@"SYNC AGAIN" forState:UIControlStateNormal];
[self.syncAgainButton setTitleColor:UIColor.whiteColor forState:UIControlStateNormal]; [self.syncAgainButton setTitleColor:UIColor.whiteColor forState:UIControlStateNormal];
self.syncAgainButton.enabled = NO; self.syncAgainButton.enabled = NO;
[self.syncAgainButton addTarget:self
action:@selector(onSyncAgain)
forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:self.syncAgainButton]; [self.view addSubview:self.syncAgainButton];
...@@ -472,5 +520,45 @@ typedef NS_ENUM(NSInteger, HistorySyncStatus) { ...@@ -472,5 +520,45 @@ typedef NS_ENUM(NSInteger, HistorySyncStatus) {
[self.tableView reloadData]; [self.tableView reloadData];
} }
- (void)onSyncAgain {
for (HistorySyncItem *item in self.allItems) {
if (item.status == HistorySyncStatusFailed) {
[[OfflineSyncManager shared] retryHistoryItem:item.raw];
}
}
NSLog(@"Starting sync again");
// [[OfflineSyncManager shared] startManualSync];
[self loadHistoryData];
[self applyFilter];
}
- (void)dumpHistoryToFile:(id)history {
NSString *docDir =
NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask,
YES).firstObject;
NSString *path =
[docDir stringByAppendingPathComponent:@"sync_history_dump.txt"];
NSString *content =
[NSString stringWithFormat:@"=== SYNC HISTORY DUMP ===\n%@\n",
history];
NSError *error = nil;
BOOL success =
[content writeToFile:path
atomically:YES
encoding:NSUTF8StringEncoding
error:&error];
if (success) {
NSLog(@"📄 History dumped to: %@", path);
} else {
NSLog(@"❌ Failed to dump history: %@", error);
}
}
@end @end
...@@ -195,18 +195,18 @@ typedef NS_ENUM(NSInteger, SyncPendingViewMode) { ...@@ -195,18 +195,18 @@ typedef NS_ENUM(NSInteger, SyncPendingViewMode) {
self.titleLabel.translatesAutoresizingMaskIntoConstraints = NO; self.titleLabel.translatesAutoresizingMaskIntoConstraints = NO;
[self.headerView addSubview:self.titleLabel]; [self.headerView addSubview:self.titleLabel];
self.shareButton = [UIButton buttonWithType:UIButtonTypeSystem]; // self.shareButton = [UIButton buttonWithType:UIButtonTypeSystem];
[self.shareButton setTitle:@"Share to Email" forState:UIControlStateNormal]; // [self.shareButton setTitle:@"Share to Email" forState:UIControlStateNormal];
[self.shareButton setTitleColor:UIColor.whiteColor forState:UIControlStateNormal]; // [self.shareButton setTitleColor:UIColor.whiteColor forState:UIControlStateNormal];
self.shareButton.layer.borderColor = UIColor.whiteColor.CGColor; // self.shareButton.layer.borderColor = UIColor.whiteColor.CGColor;
self.shareButton.layer.borderWidth = 0.5; // self.shareButton.layer.borderWidth = 0.5;
self.shareButton.layer.cornerRadius = 14; // self.shareButton.layer.cornerRadius = 14;
[self.shareButton addTarget:self // [self.shareButton addTarget:self
action:@selector(onShare) // action:@selector(onShare)
forControlEvents:UIControlEventTouchUpInside]; // forControlEvents:UIControlEventTouchUpInside];
self.shareButton.titleLabel.font = [UIFont systemFontOfSize:14]; // self.shareButton.titleLabel.font = [UIFont systemFontOfSize:14];
self.shareButton.translatesAutoresizingMaskIntoConstraints = NO; // self.shareButton.translatesAutoresizingMaskIntoConstraints = NO;
[self.headerView addSubview:self.shareButton]; // [self.headerView addSubview:self.shareButton];
[NSLayoutConstraint activateConstraints:@[ [NSLayoutConstraint activateConstraints:@[
[self.headerView.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor], [self.headerView.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor],
...@@ -222,10 +222,10 @@ typedef NS_ENUM(NSInteger, SyncPendingViewMode) { ...@@ -222,10 +222,10 @@ typedef NS_ENUM(NSInteger, SyncPendingViewMode) {
[self.titleLabel.centerXAnchor constraintEqualToAnchor:self.headerView.centerXAnchor], [self.titleLabel.centerXAnchor constraintEqualToAnchor:self.headerView.centerXAnchor],
[self.titleLabel.centerYAnchor constraintEqualToAnchor:self.headerView.centerYAnchor], [self.titleLabel.centerYAnchor constraintEqualToAnchor:self.headerView.centerYAnchor],
[self.shareButton.trailingAnchor constraintEqualToAnchor:self.headerView.trailingAnchor constant:-8], // [self.shareButton.trailingAnchor constraintEqualToAnchor:self.headerView.trailingAnchor constant:-8],
[self.shareButton.centerYAnchor constraintEqualToAnchor:self.headerView.centerYAnchor], // [self.shareButton.centerYAnchor constraintEqualToAnchor:self.headerView.centerYAnchor],
[self.shareButton.heightAnchor constraintEqualToConstant:28], // [self.shareButton.heightAnchor constraintEqualToConstant:28],
[self.shareButton.widthAnchor constraintEqualToConstant:120], // [self.shareButton.widthAnchor constraintEqualToConstant:120],
]]; ]];
} }
......
...@@ -14,7 +14,7 @@ typedef NS_ENUM(NSInteger, SyncProjectScreenState) { ...@@ -14,7 +14,7 @@ typedef NS_ENUM(NSInteger, SyncProjectScreenState) {
static NSString * const kSyncSelectedUnitsKey = @"SYNC_SELECTEDUNITS"; static NSString * const kSyncSelectedUnitsKey = @"SYNC_SELECTEDUNITS";
static NSString * const kProjectListKey = @"PROJECT_LIST"; static NSString * const kProjectListKey = @"PROJECT_LIST";
@interface SyncUnit : NSObject @interface SyncUnit : NSObject <NSSecureCoding>
@property (nonatomic, strong) NSString *planId; @property (nonatomic, strong) NSString *planId;
@property (nonatomic, strong) NSString *displayName; @property (nonatomic, strong) NSString *displayName;
@property (nonatomic, strong) NSDictionary *project; @property (nonatomic, strong) NSDictionary *project;
...@@ -24,6 +24,33 @@ static NSString * const kProjectListKey = @"PROJECT_LIST"; ...@@ -24,6 +24,33 @@ static NSString * const kProjectListKey = @"PROJECT_LIST";
@end @end
@implementation SyncUnit @implementation SyncUnit
+ (BOOL)supportsSecureCoding {
return YES;
}
- (void)encodeWithCoder:(NSCoder *)coder {
[coder encodeObject:self.planId forKey:@"planId"];
[coder encodeObject:self.displayName forKey:@"displayName"];
[coder encodeObject:self.project forKey:@"project"];
[coder encodeObject:self.projectId forKey:@"projectId"];
[coder encodeObject:self.projectName forKey:@"projectName"];
[coder encodeBool:self.selected forKey:@"selected"];
}
- (instancetype)initWithCoder:(NSCoder *)coder {
self = [super init];
if (self) {
_planId = [coder decodeObjectOfClass:[NSString class] forKey:@"planId"];
_displayName = [coder decodeObjectOfClass:[NSString class] forKey:@"displayName"];
_project = [coder decodeObjectOfClass:[NSDictionary class] forKey:@"project"];
_projectId = [coder decodeObjectOfClass:[NSString class] forKey:@"projectId"];
_projectName = [coder decodeObjectOfClass:[NSString class] forKey:@"projectName"];
_selected = [coder decodeBoolForKey:@"selected"];
}
return self;
}
@end @end
@interface SyncProject : NSObject @interface SyncProject : NSObject
...@@ -445,15 +472,15 @@ didSelectItemAtIndexPath:(NSIndexPath *)indexPath { ...@@ -445,15 +472,15 @@ didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
return; return;
} }
NSLog(@"🌐 No cached PROJECT_LIST, fetching from API"); NSLog(@"🌐 No cached PROJECT_LIST, requesting from API");
[self fetchProjectList]; [self requestProjectList];
} }
- (void)fetchProjectList { - (void)requestProjectList {
[self showLoading:YES]; [self showLoading:YES];
[APIClient fetchProjectList:^(NSDictionary *data, NSError *error) { [APIClient requestProjectList:^(NSDictionary *data, NSError *error) {
// debug // debug
NSString *plainText = [NSString stringWithFormat:@"%@", data]; NSString *plainText = [NSString stringWithFormat:@"%@", data];
...@@ -648,8 +675,11 @@ didSelectItemAtIndexPath:(NSIndexPath *)indexPath { ...@@ -648,8 +675,11 @@ didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
- (void)onDone { - (void)onDone {
NSLog(@"Selected units count: %lu", NSLog(@"Selected units count: %lu",
(unsigned long)self.selectedUnits.count); (unsigned long)self.selectedUnits.count);
[LocalStorage saveArray:self.selectedUnits NSMutableArray *dictArray = [NSMutableArray array];
forKey:@"SELECTED_OFFLINE_UNITS"]; for (SyncUnit *unit in self.selectedUnits) {
[dictArray addObject:[self dictionaryFromUnit:unit]];
}
[LocalStorage saveArray:dictArray forKey:@"SELECTED_OFFLINE_UNITS"];
[self showOfflineUnit]; [self showOfflineUnit];
} }
......
...@@ -5,8 +5,8 @@ ...@@ -5,8 +5,8 @@
#import "SyncHistoryViewController.h" #import "SyncHistoryViewController.h"
#import "SyncPendingViewController.h" #import "SyncPendingViewController.h"
#import "SyncProjectViewController.h" #import "SyncProjectViewController.h"
#import "SyncEngine.h"
#import "LocalStorage.h" #import "LocalStorage.h"
#import "OfflineSyncManager.h"
typedef NS_ENUM(NSInteger, SyncCardType) { typedef NS_ENUM(NSInteger, SyncCardType) {
SyncCardTypeOfflineUnits = 1, SyncCardTypeOfflineUnits = 1,
...@@ -14,6 +14,8 @@ typedef NS_ENUM(NSInteger, SyncCardType) { ...@@ -14,6 +14,8 @@ typedef NS_ENUM(NSInteger, SyncCardType) {
SyncCardTypeHistory SyncCardTypeHistory
}; };
static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE";
@interface SyncScreenViewController () @interface SyncScreenViewController ()
@property (nonatomic, strong) UIView *headerView; @property (nonatomic, strong) UIView *headerView;
...@@ -71,7 +73,7 @@ typedef NS_ENUM(NSInteger, SyncCardType) { ...@@ -71,7 +73,7 @@ typedef NS_ENUM(NSInteger, SyncCardType) {
self.isSyncing = NO; self.isSyncing = NO;
//placeholders //placeholders
self.pendingItemCount = 0; self.pendingItemCount = 1;
self.offlineUnitCount = 1; self.offlineUnitCount = 1;
self.historyCount = 1; self.historyCount = 1;
...@@ -87,6 +89,7 @@ typedef NS_ENUM(NSInteger, SyncCardType) { ...@@ -87,6 +89,7 @@ typedef NS_ENUM(NSInteger, SyncCardType) {
self.pendingItemCount = [self loadPendingItemCount]; self.pendingItemCount = [self loadPendingItemCount];
self.offlineUnitCount = [self loadOfflineUnitCount]; self.offlineUnitCount = [self loadOfflineUnitCount];
self.historyCount = [self loadHistoryItemCount];
[self refreshUI]; [self refreshUI];
} }
...@@ -126,18 +129,9 @@ typedef NS_ENUM(NSInteger, SyncCardType) { ...@@ -126,18 +129,9 @@ typedef NS_ENUM(NSInteger, SyncCardType) {
} }
- (void)updateLastUpdatedLabel { - (void)updateLastUpdatedLabel {
if (!self.lastUpdatedDate) { NSString *dateString = [self lastSyncDisplayString];
self.lastUpdatedLabel.text = @"Last Updated:\n--";
return;
}
NSDateFormatter *fmt = [[NSDateFormatter alloc] init];
fmt.dateStyle = NSDateFormatterMediumStyle;
fmt.timeStyle = NSDateFormatterShortStyle;
self.lastUpdatedLabel.text = self.lastUpdatedLabel.text =
[NSString stringWithFormat:@"Last Updated:\n%@", [NSString stringWithFormat:@"Last Updated:\n%@", dateString];
[fmt stringFromDate:self.lastUpdatedDate]];
} }
#pragma mark - Header #pragma mark - Header
...@@ -500,7 +494,6 @@ typedef NS_ENUM(NSInteger, SyncCardType) { ...@@ -500,7 +494,6 @@ typedef NS_ENUM(NSInteger, SyncCardType) {
} }
- (void)onOfflineUnitsTapped { - (void)onOfflineUnitsTapped {
if (self.offlineUnitCount == 0) return;
NSLog(@"Offline Units tapped"); NSLog(@"Offline Units tapped");
SyncProjectViewController *nextVC = [[SyncProjectViewController alloc] init]; SyncProjectViewController *nextVC = [[SyncProjectViewController alloc] init];
...@@ -570,39 +563,48 @@ typedef NS_ENUM(NSInteger, SyncCardType) { ...@@ -570,39 +563,48 @@ typedef NS_ENUM(NSInteger, SyncCardType) {
[self refreshUI]; [self refreshUI];
[self showProgressOverlay]; [self showProgressOverlay];
// SyncEngine *engine = [SyncEngine new]; __weak typeof(self) weakSelf = self;
// engine.submitPending = self.shouldSubmitPending;
// engine.downloadUnits = self.shouldDownloadUnits; // 1️⃣ Manual pending sync
// if (self.shouldSubmitPending) {
// __weak typeof(self) weakSelf = self; NSLog(@"Syncing Pending Items");
//
// [engine startWithProgress:^(NSInteger step, [[OfflineSyncManager shared]
// NSInteger total, processNextItemWithProgress:^(NSInteger step, NSInteger total, NSString *title) {
// NSString *title) {
// self.progressLabel.text =
// weakSelf.progressLabel.text = [NSString stringWithFormat:@"%ld / %ld", step, total];
// [NSString stringWithFormat:@"%@ (%ld/%ld)", self.titleLabel.text = title;
// title, (long)step, (long)total]; self.progressBar.progress =
// total > 0 ? (float)step / (float)total : 0;
// weakSelf.progressBar.progress =
// (float)step / (float)total; } completion:^(BOOL finished) {
//
// } completion:^(BOOL success, NSError *error) { self.pendingItemCount = [self loadPendingItemCount];
// self.historyCount = [self loadHistoryItemCount];
// [weakSelf.syncProgressOverlay removeFromSuperview]; [self refreshUI];
// weakSelf.syncProgressOverlay = nil; }];
// }
// weakSelf.isSyncing = NO;
// // 2️⃣ Unit + image download sync
// if (success) { if (self.shouldDownloadUnits) {
// weakSelf.lastUpdatedDate = [NSDate date]; NSLog(@"Downloading Units");
// } else {
// NSLog(@"❌ Sync failed: %@", error.localizedDescription); [[OfflineSyncManager shared]
// // TODO: show alert startDownloadUnitImagesWithProgress:^(NSInteger completed, NSInteger total) {
// }
// weakSelf.progressLabel.text =
// [weakSelf refreshUI]; [NSString stringWithFormat:@"Downloading Units (%ld/%ld)",
// }]; (long)completed, (long)total];
weakSelf.progressBar.progress =
total > 0 ? (float)completed / (float)total : 0;
} completion:^(BOOL success) {
[weakSelf finishSyncWithSuccess:success];
}];
}
} }
- (void)dismissSyncModal { - (void)dismissSyncModal {
...@@ -630,4 +632,38 @@ typedef NS_ENUM(NSInteger, SyncCardType) { ...@@ -630,4 +632,38 @@ typedef NS_ENUM(NSInteger, SyncCardType) {
return units.count; return units.count;
} }
- (NSInteger)loadHistoryItemCount {
NSArray *units = [LocalStorage loadArrayForKey:@"SYNC_HISTORY_QUEUE"];
return units.count;
}
- (void)finishSyncWithSuccess:(BOOL)success {
[self.syncProgressOverlay removeFromSuperview];
self.syncProgressOverlay = nil;
self.isSyncing = NO;
if (success) {
self.lastUpdatedDate = [NSDate date];
}
[self refreshUI];
}
- (NSString *)lastSyncDisplayString {
NSNumber *ts = [LocalStorage loadObjectForKey:kLastOfflineSyncDateKey];
if (!ts) {
return @"--";
}
NSDate *date = [NSDate dateWithTimeIntervalSince1970:ts.doubleValue];
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.dateStyle = NSDateFormatterMediumStyle;
formatter.timeStyle = NSDateFormatterShortStyle;
return [formatter stringFromDate:date];
}
@end @end
...@@ -6,12 +6,12 @@ NS_ASSUME_NONNULL_BEGIN ...@@ -6,12 +6,12 @@ NS_ASSUME_NONNULL_BEGIN
@interface APIClient : NSObject @interface APIClient : NSObject
+ (void)fetchAccessItem:(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;
+ (void)fetchGetUnitPlan:(void (^)(NSDictionary * _Nullable data, NSError * _Nullable error))completion; + (void)requestGetUnitPlan:(void (^)(NSDictionary * _Nullable data, NSError * _Nullable error))completion;
+ (void)fetchSettingsByLocation:(NSString * _Nonnull)locationID + (void)requestSettingsByLocation:(NSString * _Nonnull)locationID
projectID:(NSString * _Nonnull)projectID projectID:(NSString * _Nonnull)projectID
completion:(void (^)(NSDictionary * _Nullable data, NSError * _Nullable error))completion; completion:(void (^)(NSDictionary * _Nullable data, NSError * _Nullable error))completion;
...@@ -23,9 +23,9 @@ NS_ASSUME_NONNULL_BEGIN ...@@ -23,9 +23,9 @@ NS_ASSUME_NONNULL_BEGIN
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;
+ (void)fetchAppointment:(void (^)(NSDictionary * _Nullable data, NSError * _Nullable error))completion; + (void)requestAppointment:(void (^)(NSDictionary * _Nullable data, NSError * _Nullable error))completion;
+ (void)fetchGeneralInfo:(void (^)(NSDictionary * _Nullable data, NSError * _Nullable error))completion; + (void)requestGeneralInfo:(void (^)(NSDictionary * _Nullable data, NSError * _Nullable error))completion;
+ (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;
...@@ -33,15 +33,15 @@ NS_ASSUME_NONNULL_BEGIN ...@@ -33,15 +33,15 @@ NS_ASSUME_NONNULL_BEGIN
+ (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;
+ (void)fetchCancelAppointment:(id)appointmentId + (void)requestCancelAppointment:(id)appointmentId
reason:(NSString *)reason reason:(NSString *)reason
completion:(void (^)(NSDictionary * _Nullable data, NSError * _Nullable error))completion; completion:(void (^)(NSDictionary * _Nullable data, NSError * _Nullable error))completion;
+ (void)fetchDashboardInfo:(void (^)(NSDictionary *data, NSError *error))completion; + (void)requestDashboardInfo:(void (^)(NSDictionary *data, NSError *error))completion;
+ (void)fetchAnnouncement:(void (^)(NSDictionary *data, NSError *error))completion; + (void)requestAnnouncement:(void (^)(NSDictionary *data, NSError *error))completion;
+ (void)fetchHistory:(NSString *)issueID + (void)requestHistory:(NSString *)issueID
completion:(void (^)(NSDictionary *data, NSError *error))completion; completion:(void (^)(NSDictionary *data, NSError *error))completion;
+(void)submitAddInfo:(NSString *)issueID +(void)submitAddInfo:(NSString *)issueID
...@@ -55,23 +55,29 @@ NS_ASSUME_NONNULL_BEGIN ...@@ -55,23 +55,29 @@ NS_ASSUME_NONNULL_BEGIN
mediaType:(NSString *)mediaType mediaType:(NSString *)mediaType
completion:(void (^)(NSDictionary *data, NSError *error))completion; completion:(void (^)(NSDictionary *data, NSError *error))completion;
+ (void)fetchIssues:(void (^)(NSDictionary *data, NSError *error))completion; + (void)requestIssues:(void (^)(NSDictionary *data, NSError *error))completion;
+ (void)fetchVoidIssue:(id)issueID + (void)requestVoidIssue:(id)issueID
remarks:(NSString *)remarks remarks:(NSString *)remarks
completion:(void (^)(NSDictionary *data, NSError *error))completion; completion:(void (^)(NSDictionary *data, NSError *error))completion;
+ (void)fetchDeleteIssue:(id)issueID + (void)requestDeleteIssue:(id)issueID
remarks:(NSString *)remarks remarks:(NSString *)remarks
completion:(void (^)(NSDictionary *data, NSError *error))completion; completion:(void (^)(NSDictionary *data, NSError *error))completion;
+ (void)fetchCommonArea:(void (^)(NSDictionary * _Nullable data, NSError * _Nullable error))completion; + (void)requestCommonArea:(void (^)(NSDictionary * _Nullable data, NSError * _Nullable error))completion;
+ (void)fetchGeneralSettings:(void (^)(NSDictionary * _Nullable data, NSError * _Nullable error))completion; + (void)requestGeneralSettings:(void (^)(NSDictionary * _Nullable data, NSError * _Nullable error))completion;
+ (void)fetchDefectMatrix:(void (^)(NSDictionary * _Nullable data, NSError * _Nullable error))completion; + (void)requestDefectMatrix:(void (^)(NSDictionary * _Nullable data, NSError * _Nullable error))completion;
+ (void)fetchProjectList:(void (^)(NSDictionary * _Nullable data, NSError * _Nullable error))completion; + (void)requestProjectList:(void (^)(NSDictionary * _Nullable data, NSError * _Nullable error))completion;
+ (void)requestArrayImage:(NSArray *)imageArray
completion:(void (^)(NSDictionary *data, NSError *error))completion;
+ (void)downloadAndCacheImage:(NSURL *)url
completion:(void (^)(BOOL success))completion;
@end @end
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
@implementation APIClient @implementation APIClient
+ (void)fetchAccessItem:(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"; NSString *urlString = @"https://kitadev.commudesk.com/api/owner/plan/getAccessItem?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6ImF0aGlyYWh6YWlkaUBjb252ZXAuY29tIiwicGFzc3dvcmQiOiIkMnkkMTAkNU9xYklqMnZ6UHJwckJrcVd5c3I2LmJCc1hVYS9Hd014eUhnL3RhUUhPUkNQcGdmTG8yakciLCJzdWIiOjIxNzAsImlzcyI6Imh0dHBzOi8va2l0YWRldi5jb21tdWRlc2suY29tL2FwaS9vd25lci9hdXRoL3Bhc3N3b3JkbGVzc19sb2dpbiIsImlhdCI6MTc2MDMxNTM1MiwiZXhwIjoyMDc1ODg0ODcyLCJuYmYiOjE3NjAzMTUzNTIsImp0aSI6IktoQmNyQnJEdDVNdDZlWmMifQ.JnxGbZ7hTeoOr6oibIzZMphgyKtTo2Aq9Efx6mrGfFA";
...@@ -79,7 +79,7 @@ ...@@ -79,7 +79,7 @@
[task resume]; [task resume];
} }
+ (void)fetchGetUnitPlan:(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"; NSString *urlString = @"https://kitadev.commudesk.com/api/owner/plan/getLocationByUnit?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6ImF0aGlyYWh6YWlkaUBjb252ZXAuY29tIiwicGFzc3dvcmQiOiIkMnkkMTAkNU9xYklqMnZ6UHJwckJrcVd5c3I2LmJCc1hVYS9Hd014eUhnL3RhUUhPUkNQcGdmTG8yakciLCJzdWIiOjIxNzAsImlzcyI6Imh0dHBzOi8va2l0YWRldi5jb21tdWRlc2suY29tL2FwaS9vd25lci9hdXRoL3Bhc3N3b3JkbGVzc19sb2dpbiIsImlhdCI6MTc2MDMxNTM1MiwiZXhwIjoyMDc1ODg0ODcyLCJuYmYiOjE3NjAzMTUzNTIsImp0aSI6IktoQmNyQnJEdDVNdDZlWmMifQ.JnxGbZ7hTeoOr6oibIzZMphgyKtTo2Aq9Efx6mrGfFA";
...@@ -88,7 +88,7 @@ ...@@ -88,7 +88,7 @@
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST"; request.HTTPMethod = @"POST";
// 🧱 multipart/form-data setup (same as fetchAnnouncement) // 🧱 multipart/form-data setup (same as requestAnnouncement)
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];
[request setValue:contentType forHTTPHeaderField:@"Content-Type"]; [request setValue:contentType forHTTPHeaderField:@"Content-Type"];
...@@ -120,8 +120,8 @@ ...@@ -120,8 +120,8 @@
request.HTTPBody = body; request.HTTPBody = body;
// 🌍 Debug logs // 🌍 Debug logs
NSLog(@"🌍 [fetchUnitPlan] URL: %@", urlString); NSLog(@"🌍 [requestUnitPlan] URL: %@", urlString);
NSLog(@"📦 [fetchUnitPlan] Body:\n%@", [[NSString alloc] initWithData:body encoding:NSUTF8StringEncoding]); NSLog(@"📦 [requestUnitPlan] Body:\n%@", [[NSString alloc] initWithData:body encoding:NSUTF8StringEncoding]);
// 🌐 Perform request // 🌐 Perform request
NSURLSessionDataTask *task = [[NSURLSession sharedSession] NSURLSessionDataTask *task = [[NSURLSession sharedSession]
...@@ -129,7 +129,7 @@ ...@@ -129,7 +129,7 @@
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (error) { if (error) {
NSLog(@"❌ [fetchUnitPlan] Network error: %@", error); NSLog(@"❌ [requestUnitPlan] Network error: %@", error);
if (completion) completion(nil, error); if (completion) completion(nil, error);
return; return;
} }
...@@ -138,16 +138,16 @@ ...@@ -138,16 +138,16 @@
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonErr]; NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonErr];
if (jsonErr) { if (jsonErr) {
NSLog(@"❌ [fetchUnitPlan] JSON parse error: %@", jsonErr); NSLog(@"❌ [requestUnitPlan] JSON parse error: %@", jsonErr);
if (completion) completion(nil, jsonErr); if (completion) completion(nil, jsonErr);
return; return;
} }
NSLog(@"✅ [fetchUnitPlan] Response:\n%@", json); NSLog(@"✅ [requestUnitPlan] Response:\n%@", json);
NSDictionary *appData = json[@"AppData"]; NSDictionary *appData = json[@"AppData"];
if ([appData[@"status"] isEqualToString:@"success"]) { if ([appData[@"status"] isEqualToString:@"success"]) {
NSLog(@"📄 Successfully fetched unit plan for ID"); NSLog(@"📄 Successfully requested unit plan for ID");
} else if (appData[@"error_code"] && [appData[@"error_code"] integerValue] == 503) { } else if (appData[@"error_code"] && [appData[@"error_code"] integerValue] == 503) {
NSLog(@"⚠️ Server under maintenance (503)"); NSLog(@"⚠️ Server under maintenance (503)");
} else { } else {
...@@ -165,7 +165,7 @@ ...@@ -165,7 +165,7 @@
[task resume]; [task resume];
} }
+ (void)fetchSettingsByLocation:(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"; NSString *urlString = @"https://kitadev.commudesk.com/api/issue/getGeneralSettingByLocation?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6ImF0aGlyYWh6YWlkaUBjb252ZXAuY29tIiwicGFzc3dvcmQiOiIkMnkkMTAkNU9xYklqMnZ6UHJwckJrcVd5c3I2LmJCc1hVYS9Hd014eUhnL3RhUUhPUkNQcGdmTG8yakciLCJzdWIiOjIxNzAsImlzcyI6Imh0dHBzOi8va2l0YWRldi5jb21tdWRlc2suY29tL2FwaS9vd25lci9hdXRoL3Bhc3N3b3JkbGVzc19sb2dpbiIsImlhdCI6MTc2MDMxNTM1MiwiZXhwIjoyMDc1ODg0ODcyLCJuYmYiOjE3NjAzMTUzNTIsImp0aSI6IktoQmNyQnJEdDVNdDZlWmMifQ.JnxGbZ7hTeoOr6oibIzZMphgyKtTo2Aq9Efx6mrGfFA";
...@@ -206,8 +206,8 @@ ...@@ -206,8 +206,8 @@
request.HTTPBody = body; request.HTTPBody = body;
// 🌍 Debug logs // 🌍 Debug logs
NSLog(@"🌍 [fetchSettingsByLocation] URL: %@", urlString); NSLog(@"🌍 [requestSettingsByLocation] URL: %@", urlString);
NSLog(@"📦 [fetchSettingsByLocation] Body:\n%@", [[NSString alloc] initWithData:body encoding:NSUTF8StringEncoding]); NSLog(@"📦 [requestSettingsByLocation] Body:\n%@", [[NSString alloc] initWithData:body encoding:NSUTF8StringEncoding]);
// 🌐 Send request // 🌐 Send request
NSURLSessionDataTask *task = [[NSURLSession sharedSession] NSURLSessionDataTask *task = [[NSURLSession sharedSession]
...@@ -215,7 +215,7 @@ ...@@ -215,7 +215,7 @@
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (error) { if (error) {
NSLog(@"❌ [fetchSettingsByLocation] Network error: %@", error); NSLog(@"❌ [requestSettingsByLocation] Network error: %@", error);
if (completion) completion(nil, error); if (completion) completion(nil, error);
return; return;
} }
...@@ -228,13 +228,13 @@ ...@@ -228,13 +228,13 @@
return; return;
} }
NSLog(@"✅ [fetchSettingsByLocation] Response:\n%@", json); NSLog(@"✅ [requestSettingsByLocation] Response:\n%@", json);
NSDictionary *appData = json[@"AppData"]; NSDictionary *appData = json[@"AppData"];
NSString *status = appData[@"status"]; NSString *status = appData[@"status"];
if ([status isEqualToString:@"success"]) { if ([status isEqualToString:@"success"]) {
NSLog(@"📄 Successfully fetched settings for location"); NSLog(@"📄 Successfully requested settings for location");
} }
else if ([appData[@"error_code"] integerValue] == 503) { else if ([appData[@"error_code"] integerValue] == 503) {
NSLog(@"⚠️ Maintenance mode (503)"); NSLog(@"⚠️ Maintenance mode (503)");
...@@ -451,7 +451,7 @@ ...@@ -451,7 +451,7 @@
[task resume]; [task resume];
} }
+ (void)fetchAppointment:(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"; NSString *urlString = @"https://kitadev.commudesk.com/api/owner/appointment/getSlot?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6ImF0aGlyYWh6YWlkaUBjb252ZXAuY29tIiwicGFzc3dvcmQiOiIkMnkkMTAkNU9xYklqMnZ6UHJwckJrcVd5c3I2LmJCc1hVYS9Hd014eUhnL3RhUUhPUkNQcGdmTG8yakciLCJzdWIiOjIxNzAsImlzcyI6Imh0dHBzOi8va2l0YWRldi5jb21tdWRlc2suY29tL2FwaS9vd25lci9hdXRoL3Bhc3N3b3JkbGVzc19sb2dpbiIsImlhdCI6MTc2MDMxNTM1MiwiZXhwIjoyMDc1ODg0ODcyLCJuYmYiOjE3NjAzMTUzNTIsImp0aSI6IktoQmNyQnJEdDVNdDZlWmMifQ.JnxGbZ7hTeoOr6oibIzZMphgyKtTo2Aq9Efx6mrGfFA";
NSURL *url = [NSURL URLWithString:urlString]; NSURL *url = [NSURL URLWithString:urlString];
...@@ -516,7 +516,7 @@ ...@@ -516,7 +516,7 @@
[task resume]; [task resume];
} }
+ (void)fetchGeneralInfo:(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"; NSString *urlString = @"https://kitadev.commudesk.com/api/owner/plan/getUnitInfo?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6ImF0aGlyYWh6YWlkaUBjb252ZXAuY29tIiwicGFzc3dvcmQiOiIkMnkkMTAkNU9xYklqMnZ6UHJwckJrcVd5c3I2LmJCc1hVYS9Hd014eUhnL3RhUUhPUkNQcGdmTG8yakciLCJzdWIiOjIxNzAsImlzcyI6Imh0dHBzOi8va2l0YWRldi5jb21tdWRlc2suY29tL2FwaS9vd25lci9hdXRoL3Bhc3N3b3JkbGVzc19sb2dpbiIsImlhdCI6MTc2MDMxNTM1MiwiZXhwIjoyMDc1ODg0ODcyLCJuYmYiOjE3NjAzMTUzNTIsImp0aSI6IktoQmNyQnJEdDVNdDZlWmMifQ.JnxGbZ7hTeoOr6oibIzZMphgyKtTo2Aq9Efx6mrGfFA";
NSURL *url = [NSURL URLWithString:urlString]; NSURL *url = [NSURL URLWithString:urlString];
...@@ -707,7 +707,7 @@ ...@@ -707,7 +707,7 @@
[task resume]; [task resume];
} }
+ (void)fetchCancelAppointment:(id)appointmentId + (void)requestCancelAppointment:(id)appointmentId
reason:(NSString *)reason reason:(NSString *)reason
completion:(void (^)(NSDictionary *data, NSError *error))completion { completion:(void (^)(NSDictionary *data, NSError *error))completion {
// ✅ URL // ✅ URL
...@@ -763,7 +763,7 @@ ...@@ -763,7 +763,7 @@
if (jsonParseError) { if (jsonParseError) {
NSLog(@"⚠️ JSON Parse Error: %@", jsonParseError); NSLog(@"⚠️ JSON Parse Error: %@", jsonParseError);
} else { } else {
NSLog(@"✅ Fetch Cancel Appointment Response:\n%@", jsonResponse); NSLog(@"✅ request Cancel Appointment Response:\n%@", jsonResponse);
} }
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
...@@ -774,7 +774,7 @@ ...@@ -774,7 +774,7 @@
[task resume]; [task resume];
} }
+ (void)fetchDashboardInfo:(void (^)(NSDictionary *data, NSError *error))completion { + (void)requestDashboardInfo:(void (^)(NSDictionary *data, NSError *error))completion {
NSString *urlString = @"https://kitadev.commudesk.com/api/owner/plan/getIssueUpdateAppointmentInfo"; NSString *urlString = @"https://kitadev.commudesk.com/api/owner/plan/getIssueUpdateAppointmentInfo";
NSURL *url = [NSURL URLWithString:urlString]; NSURL *url = [NSURL URLWithString:urlString];
...@@ -837,7 +837,7 @@ ...@@ -837,7 +837,7 @@
[task resume]; [task resume];
} }
+ (void)fetchAnnouncement:(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"; NSString *urlString = @"https://kitadev.commudesk.com/api/clientAnnouncement?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6ImF0aGlyYWh6YWlkaUBjb252ZXAuY29tIiwicGFzc3dvcmQiOiIkMnkkMTAkNU9xYklqMnZ6UHJwckJrcVd5c3I2LmJCc1hVYS9Hd014eUhnL3RhUUhPUkNQcGdmTG8yakciLCJzdWIiOjIxNzAsImlzcyI6Imh0dHBzOi8va2l0YWRldi5jb21tdWRlc2suY29tL2FwaS9vd25lci9hdXRoL3Bhc3N3b3JkbGVzc19sb2dpbiIsImlhdCI6MTc2MDMxNTM1MiwiZXhwIjoyMDc1ODg0ODcyLCJuYmYiOjE3NjAzMTUzNTIsImp0aSI6IktoQmNyQnJEdDVNdDZlWmMifQ.JnxGbZ7hTeoOr6oibIzZMphgyKtTo2Aq9Efx6mrGfFA";
NSURL *url = [NSURL URLWithString:urlString]; NSURL *url = [NSURL URLWithString:urlString];
...@@ -894,7 +894,7 @@ ...@@ -894,7 +894,7 @@
announcements = json[@"Data"]; announcements = json[@"Data"];
NSLog(@"📣 Got %lu announcements", (unsigned long)announcements.count); NSLog(@"📣 Got %lu announcements", (unsigned long)announcements.count);
} else { } else {
NSLog(@"⚠️ Announcement fetch failed: %@", appData[@"message"]); NSLog(@"⚠️ Announcement request failed: %@", appData[@"message"]);
} }
if (completion) { if (completion) {
...@@ -906,7 +906,7 @@ ...@@ -906,7 +906,7 @@
[task resume]; [task resume];
} }
+ (void)fetchHistory:(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"; NSString *urlString = @"https://kitadev.commudesk.com/api/owner/issue/getIssueHistory?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6ImF0aGlyYWh6YWlkaUBjb252ZXAuY29tIiwicGFzc3dvcmQiOiIkMnkkMTAkNU9xYklqMnZ6UHJwckJrcVd5c3I2LmJCc1hVYS9Hd014eUhnL3RhUUhPUkNQcGdmTG8yakciLCJzdWIiOjIxNzAsImlzcyI6Imh0dHBzOi8va2l0YWRldi5jb21tdWRlc2suY29tL2FwaS9vd25lci9hdXRoL3Bhc3N3b3JkbGVzc19sb2dpbiIsImlhdCI6MTc2MDMxNTM1MiwiZXhwIjoyMDc1ODg0ODcyLCJuYmYiOjE3NjAzMTUzNTIsImp0aSI6IktoQmNyQnJEdDVNdDZlWmMifQ.JnxGbZ7hTeoOr6oibIzZMphgyKtTo2Aq9Efx6mrGfFA";
...@@ -1175,7 +1175,7 @@ ...@@ -1175,7 +1175,7 @@
[task resume]; [task resume];
} }
+ (void)fetchIssues:(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"; NSString *urlString = @"https://kitadev.commudesk.com/api/owner/issue/getIssue?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6ImF0aGlyYWh6YWlkaUBjb252ZXAuY29tIiwicGFzc3dvcmQiOiIkMnkkMTAkNU9xYklqMnZ6UHJwckJrcVd5c3I2LmJCc1hVYS9Hd014eUhnL3RhUUhPUkNQcGdmTG8yakciLCJzdWIiOjIxNzAsImlzcyI6Imh0dHBzOi8va2l0YWRldi5jb21tdWRlc2suY29tL2FwaS9vd25lci9hdXRoL3Bhc3N3b3JkbGVzc19sb2dpbiIsImlhdCI6MTc2MDMxNTM1MiwiZXhwIjoyMDc1ODg0ODcyLCJuYmYiOjE3NjAzMTUzNTIsImp0aSI6IktoQmNyQnJEdDVNdDZlWmMifQ.JnxGbZ7hTeoOr6oibIzZMphgyKtTo2Aq9Efx6mrGfFA";
NSURL *url = [NSURL URLWithString:urlString]; NSURL *url = [NSURL URLWithString:urlString];
...@@ -1248,7 +1248,7 @@ ...@@ -1248,7 +1248,7 @@
[task resume]; [task resume];
} }
+ (void)fetchVoidIssue:(id)issueID + (void)requestVoidIssue:(id)issueID
remarks:(NSString *)remarks remarks:(NSString *)remarks
completion:(void (^)(NSDictionary *data, NSError *error))completion { completion:(void (^)(NSDictionary *data, NSError *error))completion {
// ✅ URL // ✅ URL
...@@ -1322,7 +1322,7 @@ ...@@ -1322,7 +1322,7 @@
[task resume]; [task resume];
} }
+ (void)fetchDeleteIssue:(NSString *)issueID + (void)requestDeleteIssue:(NSString *)issueID
remarks:(NSString *)remarks remarks:(NSString *)remarks
completion:(void (^)(NSDictionary *data, NSError *error))completion { completion:(void (^)(NSDictionary *data, NSError *error))completion {
// ✅ URL // ✅ URL
...@@ -1389,7 +1389,7 @@ ...@@ -1389,7 +1389,7 @@
[task resume]; [task resume];
} }
+ (void)fetchCommonArea:(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"; NSString *urlString = @"https://kitadev.commudesk.com/api/owner/plan/getCommonArea?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6ImF0aGlyYWh6YWlkaUBjb252ZXAuY29tIiwicGFzc3dvcmQiOiIkMnkkMTAkNU9xYklqMnZ6UHJwckJrcVd5c3I2LmJCc1hVYS9Hd014eUhnL3RhUUhPUkNQcGdmTG8yakciLCJzdWIiOjIxNzAsImlzcyI6Imh0dHBzOi8va2l0YWRldi5jb21tdWRlc2suY29tL2FwaS9vd25lci9hdXRoL3Bhc3N3b3JkbGVzc19sb2dpbiIsImlhdCI6MTc2MDMxNTM1MiwiZXhwIjoyMDc1ODg0ODcyLCJuYmYiOjE3NjAzMTUzNTIsImp0aSI6IktoQmNyQnJEdDVNdDZlWmMifQ.JnxGbZ7hTeoOr6oibIzZMphgyKtTo2Aq9Efx6mrGfFA";
NSURL *url = [NSURL URLWithString:urlString]; NSURL *url = [NSURL URLWithString:urlString];
...@@ -1463,9 +1463,9 @@ ...@@ -1463,9 +1463,9 @@
[task resume]; [task resume];
} }
+ (void)fetchGeneralSettings:(void (^)(NSDictionary *data, NSError *error))completion { + (void)requestGeneralSettings:(void (^)(NSDictionary *data, NSError *error))completion {
// ✅ URL // ✅ URL
NSString *urlString = @"https://kitadev.commudesk.com/api/owner/plan/getCommonArea?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6ImF0aGlyYWh6YWlkaUBjb252ZXAuY29tIiwicGFzc3dvcmQiOiIkMnkkMTAkNU9xYklqMnZ6UHJwckJrcVd5c3I2LmJCc1hVYS9Hd014eUhnL3RhUUhPUkNQcGdmTG8yakciLCJzdWIiOjIxNzAsImlzcyI6Imh0dHBzOi8va2l0YWRldi5jb21tdWRlc2suY29tL2FwaS9vd25lci9hdXRoL3Bhc3N3b3JkbGVzc19sb2dpbiIsImlhdCI6MTc2MDMxNTM1MiwiZXhwIjoyMDc1ODg0ODcyLCJuYmYiOjE3NjAzMTUzNTIsImp0aSI6IktoQmNyQnJEdDVNdDZlWmMifQ.JnxGbZ7hTeoOr6oibIzZMphgyKtTo2Aq9Efx6mrGfFA"; NSString *urlString = @"https://kitadev.commudesk.com/api/issue/getGeneralSetting?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6ImF0aGlyYWh6YWlkaUBjb252ZXAuY29tIiwicGFzc3dvcmQiOiIkMnkkMTAkNU9xYklqMnZ6UHJwckJrcVd5c3I2LmJCc1hVYS9Hd014eUhnL3RhUUhPUkNQcGdmTG8yakciLCJzdWIiOjIxNzAsImlzcyI6Imh0dHBzOi8va2l0YWRldi5jb21tdWRlc2suY29tL2FwaS9vd25lci9hdXRoL3Bhc3N3b3JkbGVzc19sb2dpbiIsImlhdCI6MTc2MDMxNTM1MiwiZXhwIjoyMDc1ODg0ODcyLCJuYmYiOjE3NjAzMTUzNTIsImp0aSI6IktoQmNyQnJEdDVNdDZlWmMifQ.JnxGbZ7hTeoOr6oibIzZMphgyKtTo2Aq9Efx6mrGfFA";
NSURL *url = [NSURL URLWithString:urlString]; NSURL *url = [NSURL URLWithString:urlString];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
...@@ -1474,12 +1474,22 @@ ...@@ -1474,12 +1474,22 @@
// ✅ JSON headers // ✅ JSON headers
[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,
} }
}; };
...@@ -1527,7 +1537,7 @@ ...@@ -1527,7 +1537,7 @@
[task resume]; [task resume];
} }
+ (void)fetchDefectMatrix:(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"; NSString *urlString = @"https://kitadev.commudesk.com/api/issue/getGeneralSettingByUnit?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6ImF0aGlyYWh6YWlkaUBjb252ZXAuY29tIiwicGFzc3dvcmQiOiIkMnkkMTAkNU9xYklqMnZ6UHJwckJrcVd5c3I2LmJCc1hVYS9Hd014eUhnL3RhUUhPUkNQcGdmTG8yakciLCJzdWIiOjIxNzAsImlzcyI6Imh0dHBzOi8va2l0YWRldi5jb21tdWRlc2suY29tL2FwaS9vd25lci9hdXRoL3Bhc3N3b3JkbGVzc19sb2dpbiIsImlhdCI6MTc2MDMxNTM1MiwiZXhwIjoyMDc1ODg0ODcyLCJuYmYiOjE3NjAzMTUzNTIsImp0aSI6IktoQmNyQnJEdDVNdDZlWmMifQ.JnxGbZ7hTeoOr6oibIzZMphgyKtTo2Aq9Efx6mrGfFA";
NSURL *url = [NSURL URLWithString:urlString]; NSURL *url = [NSURL URLWithString:urlString];
...@@ -1602,7 +1612,7 @@ ...@@ -1602,7 +1612,7 @@
[task resume]; [task resume];
} }
+ (void)fetchProjectList:(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"; NSString *urlString = @"https://kitadev.commudesk.com/api/owner/project/listProject?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6ImF0aGlyYWh6YWlkaUBjb252ZXAuY29tIiwicGFzc3dvcmQiOiIkMnkkMTAkNU9xYklqMnZ6UHJwckJrcVd5c3I2LmJCc1hVYS9Hd014eUhnL3RhUUhPUkNQcGdmTG8yakciLCJzdWIiOjIxNzAsImlzcyI6Imh0dHBzOi8va2l0YWRldi5jb21tdWRlc2suY29tL2FwaS9vd25lci9hdXRoL3Bhc3N3b3JkbGVzc19sb2dpbiIsImlhdCI6MTc2MDMxNTM1MiwiZXhwIjoyMDc1ODg0ODcyLCJuYmYiOjE3NjAzMTUzNTIsImp0aSI6IktoQmNyQnJEdDVNdDZlWmMifQ.JnxGbZ7hTeoOr6oibIzZMphgyKtTo2Aq9Efx6mrGfFA";
NSURL *url = [NSURL URLWithString:urlString]; NSURL *url = [NSURL URLWithString:urlString];
...@@ -1665,6 +1675,140 @@ ...@@ -1665,6 +1675,140 @@
[task resume]; [task resume];
} }
+ (void)requestArrayImage:(NSArray *)imageArray
completion:(void (^)(NSDictionary *data, NSError *error))completion {
// ✅ URL
NSString *urlString = @"https://kitadev.commudesk.com/api/owner/offline/syncImageArray?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6ImF0aGlyYWh6YWlkaUBjb252ZXAuY29tIiwicGFzc3dvcmQiOiIkMnkkMTAkNU9xYklqMnZ6UHJwckJrcVd5c3I2LmJCc1hVYS9Hd014eUhnL3RhUUhPUkNQcGdmTG8yakciLCJzdWIiOjIxNzAsImlzcyI6Imh0dHBzOi8va2l0YWRldi5jb21tdWRlc2suY29tL2FwaS9vd25lci9hdXRoL3Bhc3N3b3JkbGVzc19sb2dpbiIsImlhdCI6MTc2MDMxNTM1MiwiZXhwIjoyMDc1ODg0ODcyLCJuYmYiOjE3NjAzMTUzNTIsImp0aSI6IktoQmNyQnJEdDVNdDZlWmMifQ.JnxGbZ7hTeoOr6oibIzZMphgyKtTo2Aq9Efx6mrGfFA";
NSURL *url = [NSURL URLWithString:urlString];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST";
// ✅ JSON headers
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
[request setValue:@"application/json" forHTTPHeaderField:@"Accept"];
// ✅ Build JSON body
NSDictionary *jsonBody = @{
@"data": imageArray,
};
// ✅ Convert dictionary → NSData
NSError *jsonError;
NSData *bodyData = [NSJSONSerialization dataWithJSONObject:jsonBody options:0 error:&jsonError];
if (jsonError) {
NSLog(@"❌ JSON Serialization Error: %@", jsonError);
if (completion) completion(nil, jsonError);
return;
}
request.HTTPBody = bodyData;
// 🧭 Debug logs
NSLog(@"🌍 URL: %@", urlString);
NSLog(@"📦 Body JSON: %@", jsonBody);
// ✅ Make request
NSURLSessionDataTask *task = [[NSURLSession sharedSession]
dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (error) {
NSLog(@"❌ Network Error: %@", error);
dispatch_async(dispatch_get_main_queue(), ^{
if (completion) completion(nil, error);
});
return;
}
NSError *jsonParseError;
NSDictionary *jsonResponse = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonParseError];
if (jsonParseError) {
NSLog(@"⚠️ JSON Parse Error: %@", jsonParseError);
} else {
NSLog(@"✅ Project List API Response:\n%@", jsonResponse);
}
dispatch_async(dispatch_get_main_queue(), ^{
if (completion) completion(jsonResponse, jsonParseError);
});
}];
[task resume];
}
// + (void)downloadAndCacheImage:(NSURL *)url
// completion:(void (^)(BOOL success))completion {
// if (!url) {
// if (completion) completion(NO);
// return;
// }
// // 1️⃣ Build cache path
// NSString *fileName = url.lastPathComponent;
// // NSString *cacheDir = [self offlineImageCacheDirectory];
// NSString *filePath = [cacheDir stringByAppendingPathComponent:fileName];
// // 2️⃣ Skip if already cached
// if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
// NSLog(@"🟢 Cached file exists: %@", fileName);
// if (completion) completion(YES);
// return;
// }
// // 3️⃣ Build request with timeout
// NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
// request.HTTPMethod = @"GET";
// request.timeoutInterval = 20; // ⏱️ per-file timeout
// NSURLSessionConfiguration *config =
// [NSURLSessionConfiguration defaultSessionConfiguration];
// config.requestCachePolicy = NSURLRequestReloadIgnoringLocalCacheData;
// NSURLSession *session = [NSURLSession sessionWithConfiguration:config];
// // 4️⃣ Download task
// NSURLSessionDownloadTask *task =
// [session downloadTaskWithRequest:request
// completionHandler:^(NSURL *location,
// NSURLResponse *response,
// NSError *error) {
// if (error || !location) {
// NSLog(@"❌ Download failed: %@ | %@", url, error);
// dispatch_async(dispatch_get_main_queue(), ^{
// if (completion) completion(NO);
// });
// return;
// }
// // 5️⃣ Move file to cache
// NSError *moveError;
// [[NSFileManager defaultManager]
// moveItemAtURL:location
// toURL:[NSURL fileURLWithPath:filePath]
// error:&moveError];
// if (moveError) {
// NSLog(@"❌ Save failed: %@", moveError);
// dispatch_async(dispatch_get_main_queue(), ^{
// if (completion) completion(NO);
// });
// return;
// }
// NSLog(@"✅ Cached: %@", fileName);
// dispatch_async(dispatch_get_main_queue(), ^{
// if (completion) completion(YES);
// });
// }];
// [task resume];
// }
#pragma mark - Helper Methods #pragma mark - Helper Methods
+ (NSString *)multipartFormField:(NSString *)name value:(NSString *)value boundary:(NSString *)boundary { + (NSString *)multipartFormField:(NSString *)name value:(NSString *)value boundary:(NSString *)boundary {
return [NSString stringWithFormat:@"--%@\r\nContent-Disposition: form-data; name=\"data[%@]\"\r\n\r\n%@\r\n", boundary, name, value]; return [NSString stringWithFormat:@"--%@\r\nContent-Disposition: form-data; name=\"data[%@]\"\r\n\r\n%@\r\n", boundary, name, value];
......
...@@ -18,4 +18,8 @@ ...@@ -18,4 +18,8 @@
/// Save array safely (for convenience) /// Save array safely (for convenience)
+ (void)saveArray:(NSArray *)array forKey:(NSString *)key; + (void)saveArray:(NSArray *)array forKey:(NSString *)key;
+ (NSDictionary *)loadDictionaryForKey:(NSString *)key;
+ (void)saveDictionary:(NSDictionary *)dict forKey:(NSString *)key;
@end @end
...@@ -57,4 +57,16 @@ ...@@ -57,4 +57,16 @@
[self saveObject:array forKey:key]; [self saveObject:array forKey:key];
} }
+ (NSDictionary *)loadDictionaryForKey:(NSString *)key {
id object = [self loadObjectForKey:key];
if ([object isKindOfClass:[NSDictionary class]]) {
return object;
}
return nil;
}
+ (void)saveDictionary:(NSDictionary *)dict forKey:(NSString *)key {
[self saveObject:dict forKey:key];
}
@end @end
...@@ -28,9 +28,22 @@ NS_ASSUME_NONNULL_BEGIN ...@@ -28,9 +28,22 @@ NS_ASSUME_NONNULL_BEGIN
- (BOOL)hasPendingItems; - (BOOL)hasPendingItems;
- (NSArray *)pendingItemsForUI; - (NSArray *)pendingItemsForUI;
- (NSArray *)historyItemsForUI;
- (void)clearOfflineQueue; - (void)clearOfflineQueue;
- (void)processNextItemWithProgress:(void (^)(NSInteger step,
NSInteger total,
NSString *title))progress
completion:(void (^)(BOOL finished))completion;
// Retry failed history item
- (void)retryHistoryItem:(NSDictionary *)historyItem;
// Download Units
- (void)startDownloadUnitImagesWithProgress:(void (^)(NSInteger completed, NSInteger total))progress
completion:(void (^)(BOOL success))completion;
@end @end
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END
...@@ -8,6 +8,10 @@ static NSString * const kOfflineQueueKey = @"OFFLINE_SYNC_QUEUE"; ...@@ -8,6 +8,10 @@ static NSString * const kOfflineQueueKey = @"OFFLINE_SYNC_QUEUE";
static NSString * const kSelectedOfflineUnitsKey = @"SELECTED_OFFLINE_UNITS"; static NSString * const kSelectedOfflineUnitsKey = @"SELECTED_OFFLINE_UNITS";
static NSString * const kSyncHistoryKey = @"SYNC_HISTORY_QUEUE";
static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE";
@implementation OfflineSyncManager @implementation OfflineSyncManager
#pragma mark - Singleton #pragma mark - Singleton
...@@ -22,7 +26,6 @@ static NSString * const kSelectedOfflineUnitsKey = @"SELECTED_OFFLINE_UNITS"; ...@@ -22,7 +26,6 @@ static NSString * const kSelectedOfflineUnitsKey = @"SELECTED_OFFLINE_UNITS";
} }
#pragma mark - Queue Helpers (CRITICAL) #pragma mark - Queue Helpers (CRITICAL)
- (NSMutableArray *)safeLoadQueue { - (NSMutableArray *)safeLoadQueue {
id stored = [LocalStorage loadArrayForKey:kOfflineQueueKey]; id stored = [LocalStorage loadArrayForKey:kOfflineQueueKey];
...@@ -113,7 +116,6 @@ static NSString * const kSelectedOfflineUnitsKey = @"SELECTED_OFFLINE_UNITS"; ...@@ -113,7 +116,6 @@ static NSString * const kSelectedOfflineUnitsKey = @"SELECTED_OFFLINE_UNITS";
} }
#pragma mark - Core Queue Add #pragma mark - Core Queue Add
- (void)enqueueAction:(NSString *)type - (void)enqueueAction:(NSString *)type
payload:(NSDictionary *)payload payload:(NSDictionary *)payload
images:(NSArray<UIImage *> *)images images:(NSArray<UIImage *> *)images
...@@ -144,42 +146,52 @@ static NSString * const kSelectedOfflineUnitsKey = @"SELECTED_OFFLINE_UNITS"; ...@@ -144,42 +146,52 @@ static NSString * const kSelectedOfflineUnitsKey = @"SELECTED_OFFLINE_UNITS";
#pragma mark - Processing Queue #pragma mark - Processing Queue
- (void)processNextItemWithProgress:(void (^)(NSInteger step,
NSInteger total,
NSString *title))progress
completion:(void (^)(BOOL finished))completion {
- (void)startManualSync { NSMutableArray *queue = [self safeLoadQueue];
NSInteger total = queue.count;
if (![NetworkManager.sharedManager isConnected]) { if (total == 0) {
NSLog(@"🚫 [OfflineSync] No internet — aborting manual sync"); if (completion) completion(YES);
return; return;
} }
[self processNextItem]; __block NSInteger step = 0;
}
- (void)processNextItem { __weak typeof(self) weakSelf = self;
NSMutableArray *queue = [self safeLoadQueue]; void (^processNext)(void);
if (queue.count == 0) { processNext = ^{
NSLog(@"✅ [OfflineSync] Queue empty — sync complete"); NSMutableArray *currentQueue = [weakSelf safeLoadQueue];
return;
}
NSDictionary *item = queue.firstObject; if (currentQueue.count == 0) {
if (![item isKindOfClass:[NSDictionary class]]) { if (completion) completion(YES);
NSLog(@"⚠️ [OfflineSync] Invalid item — removing"); return;
[queue removeObjectAtIndex:0]; }
[self saveQueue:queue];
[self processNextItem]; NSDictionary *item = currentQueue.firstObject;
return; step++;
}
if (progress) {
NSString *title = item[@"issueDisplay"] ?: item[@"type"] ?: @"Syncing";
progress(step, total, title);
}
NSLog(@"🔄 [OfflineSync] Processing %@", item[@"type"]); [weakSelf processItem:item completion:^(BOOL success) {
[self processItem:item completion:^(BOOL success) { // 🔥 Save to history
NSMutableDictionary *historyItem = [item mutableCopy];
historyItem[@"syncedAt"] = [weakSelf todayDateString];
historyItem[@"status"] = success ? @"SUCCESS" : @"FAILED";
if (success) { NSMutableArray *history = [weakSelf safeLoadHistory];
NSLog(@"✅ [OfflineSync] Item synced"); [history insertObject:historyItem atIndex:0];
[weakSelf saveHistoryQueue:history];
// 🔥 cleanup files // 🔥 Cleanup files
for (NSString *path in item[@"imagePaths"]) { for (NSString *path in item[@"imagePaths"]) {
[[NSFileManager defaultManager] removeItemAtPath:path error:nil]; [[NSFileManager defaultManager] removeItemAtPath:path error:nil];
} }
...@@ -187,60 +199,100 @@ static NSString * const kSelectedOfflineUnitsKey = @"SELECTED_OFFLINE_UNITS"; ...@@ -187,60 +199,100 @@ static NSString * const kSelectedOfflineUnitsKey = @"SELECTED_OFFLINE_UNITS";
[[NSFileManager defaultManager] removeItemAtPath:item[@"mediaPath"] error:nil]; [[NSFileManager defaultManager] removeItemAtPath:item[@"mediaPath"] error:nil];
} }
// remove item and continue // 🔥 Remove processed item
NSMutableArray *updatedQueue = [self safeLoadQueue]; NSMutableArray *updatedQueue = [weakSelf safeLoadQueue];
if (updatedQueue.count > 0) { if (updatedQueue.count > 0) {
[updatedQueue removeObjectAtIndex:0]; [updatedQueue removeObjectAtIndex:0];
[self saveQueue:updatedQueue]; [weakSelf saveQueue:updatedQueue];
} }
[self processNextItem]; // move to next // 🔁 Continue
} else { dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"❌ [OfflineSync] Sync failed — stopping"); processNext();
// ❗ DO NOT remove item });
// user must tap Sync again }];
} };
}];
processNext();
} }
- (void)processItem:(NSDictionary *)item - (void)processItem:(NSDictionary *)item
completion:(void (^)(BOOL success))completion { completion:(void (^)(BOOL success))completion {
NSString *type = item[@"type"] ?: @""; NSString *type = item[@"type"] ?: @"";
// Load files
NSArray<UIImage *> *images = [self loadImagesFromPaths:item[@"imagePaths"]];
NSURL *mediaURL = item[@"mediaPath"] ? [NSURL fileURLWithPath:item[@"mediaPath"]] : nil;
// Define per-item timeout
NSTimeInterval timeout = 60.0; // seconds
__block BOOL completed = NO;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(timeout * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
if (!completed) {
NSLog(@"⚠️ [OfflineSync] Item timed out: %@, marking as FAILED", item[@"type"]);
completed = YES;
completion(NO);
}
});
void (^finalize)(BOOL) = ^(BOOL success){
if (!completed) {
completed = YES;
completion(success);
}
};
if ([type isEqualToString:@"ADD_INFO"]) { if ([type isEqualToString:@"ADD_INFO"]) {
NSArray *images = [self loadImagesFromPaths:item[@"imagePaths"]]; if (images.count == 0 && !mediaURL) {
NSURL *mediaURL = item[@"mediaPath"] ? [NSURL fileURLWithPath:item[@"mediaPath"]] : nil; // Simple ADD_INFO without attachments
BOOL hasImages = images.count > 0;
BOOL hasMedia = mediaURL != nil;
if (!hasImages && !hasMedia) {
[APIClient submitAddInfo:item[@"issue_id"] [APIClient submitAddInfo:item[@"issue_id"]
remarks:item[@"remarks"] remarks:item[@"remarks"]
completion:^(NSDictionary *r, NSError *e) { completion:^(NSDictionary *res, NSError *err) {
completion(e == nil); finalize(err == nil);
}]; }];
} else { } else {
// ADD_INFO with images/media
[APIClient uploadAddInfo:item[@"issue_id"] [APIClient uploadAddInfo:item[@"issue_id"]
remarks:item[@"remarks"] remarks:item[@"remarks"]
images:images images:images
mediaURL:mediaURL mediaURL:mediaURL
mediaType:item[@"mediaType"] mediaType:item[@"mediaType"]
completion:^(NSDictionary *r, NSError *e) { completion:^(NSDictionary *res, NSError *err) {
completion(e == nil); finalize(err == nil);
}]; }];
} }
return; return;
} }
NSLog(@"⚠️ Unknown offline type: %@", type); else if ([type isEqualToString:@"ADD_ISSUE"]) {
completion(NO); [APIClient submitAddIssue:item[@"payload"]
images:images
completion:^(NSDictionary *res, NSError *err){
finalize(err == nil);
}];
return;
}
else if ([type isEqualToString:@"UPDATE_ISSUE"]) {
[APIClient submitUpdateIssue:item[@"payload"]
images:images
completion:^(NSDictionary *res, NSError *err){
finalize(err == nil);
}];
return;
}
else {
NSLog(@"⚠️ [OfflineSync] Unknown offline type: %@", type);
finalize(NO);
return;
}
} }
#pragma mark - Disk Handling #pragma mark - Disk Handling
- (NSArray *)saveImagesToDisk:(NSArray<UIImage *> *)images { - (NSArray *)saveImagesToDisk:(NSArray<UIImage *> *)images {
NSMutableArray *paths = [NSMutableArray array]; NSMutableArray *paths = [NSMutableArray array];
...@@ -342,6 +394,88 @@ static NSString * const kSelectedOfflineUnitsKey = @"SELECTED_OFFLINE_UNITS"; ...@@ -342,6 +394,88 @@ static NSString * const kSelectedOfflineUnitsKey = @"SELECTED_OFFLINE_UNITS";
return sections; return sections;
} }
#pragma mark - History Helpers
- (NSMutableArray *)safeLoadHistory {
id stored = [LocalStorage loadArrayForKey:kSyncHistoryKey];
if (![stored isKindOfClass:[NSArray class]]) {
[LocalStorage removeObjectForKey:kSyncHistoryKey];
return [NSMutableArray array];
}
NSMutableArray *valid = [NSMutableArray array];
for (id obj in stored) {
if ([obj isKindOfClass:[NSDictionary class]]) {
[valid addObject:obj];
}
}
return valid;
}
- (void)saveHistory:(NSArray *)history {
[LocalStorage saveArray:history forKey:kSyncHistoryKey];
}
- (NSArray *)historyItemsForUI {
NSArray *history = [self loadHistoryQueue];
NSMutableArray *sections = [NSMutableArray array];
for (NSDictionary *item in history) {
NSDictionary *uiItem = @{
@"unitName": item[@"unitName"] ?: @"-",
@"createdBy": item[@"createdBy"] ?: @"-",
@"createdOn": item[@"createdAt"] ?: @"-",
@"uploadedOn": item[@"syncedAt"] ?: @"-",
@"type": item[@"issueDisplay"] ?: @"-",
@"status": item[@"status"] ?: @"FAILED",
@"raw": item
};
[sections addObject:uiItem];
}
return sections;
}
- (NSInteger)historyItemCount {
return [self safeLoadHistory].count;
}
- (NSMutableArray *)loadHistoryQueue {
id stored = [LocalStorage loadArrayForKey:kSyncHistoryKey];
if (![stored isKindOfClass:[NSArray class]]) {
return [NSMutableArray array];
}
NSMutableArray *valid = [NSMutableArray array];
for (id obj in stored) {
if ([obj isKindOfClass:[NSDictionary class]]) {
[valid addObject:obj];
}
}
return valid;
}
- (void)saveHistoryQueue:(NSArray *)queue {
[LocalStorage saveArray:queue forKey:kSyncHistoryKey];
}
- (void)retryHistoryItem:(NSDictionary *)historyItem {
NSMutableArray *queue = [self safeLoadQueue];
[queue addObject:historyItem];
[self saveQueue:queue];
NSMutableArray *history = [self loadHistoryQueue];
[history removeObject:historyItem];
[self saveHistoryQueue:history];
}
- (NSString *)formatTimestamp:(NSNumber *)ts { - (NSString *)formatTimestamp:(NSNumber *)ts {
NSDate *date = [NSDate dateWithTimeIntervalSince1970:ts.doubleValue]; NSDate *date = [NSDate dateWithTimeIntervalSince1970:ts.doubleValue];
NSDateFormatter *fmt = [[NSDateFormatter alloc] init]; NSDateFormatter *fmt = [[NSDateFormatter alloc] init];
...@@ -361,4 +495,283 @@ static NSString * const kSelectedOfflineUnitsKey = @"SELECTED_OFFLINE_UNITS"; ...@@ -361,4 +495,283 @@ static NSString * const kSelectedOfflineUnitsKey = @"SELECTED_OFFLINE_UNITS";
NSLog(@"🧹 [OfflineSync] Offline queue cleared"); NSLog(@"🧹 [OfflineSync] Offline queue cleared");
} }
#pragma mark - Download Units
- (void)startDownloadUnitImagesWithProgress:(void (^)(NSInteger completed, NSInteger total))progress
completion:(void (^)(BOOL success))completion {
if (![NetworkManager.sharedManager isConnected]) {
if (completion) completion(NO);
return;
}
NSString *projectId = @"8";
NSString *drawingPlanId = @"135";
NSArray *selectedUnits = [LocalStorage loadArrayForKey:kSelectedOfflineUnitsKey];
if (!selectedUnits) selectedUnits = @[];
// 1️⃣ Build the sequential task queue
NSMutableArray<void (^)(void (^next)(void))> *tasks = [NSMutableArray array];
// 2️⃣ Add 6 independent API calls as tasks
[tasks addObject:^(void (^next)(void)) {
NSString *key = [NSString stringWithFormat:@"ISSUES_%@", drawingPlanId];
[APIClient requestIssues:^(NSDictionary *res, NSError *err) {
if (res) [LocalStorage saveDictionary:res[@"Data"] forKey:key];
next();
}];
}];
[tasks addObject:^(void (^next)(void)) {
NSString *key = [NSString stringWithFormat:@"DASHBOARD_INFO_%@", drawingPlanId];
[APIClient requestDashboardInfo:^(NSDictionary *res, NSError *err) {
if (res) [LocalStorage saveDictionary:res[@"Data"] forKey:key];
next();
}];
}];
[tasks addObject:^(void (^next)(void)) {
NSString *key = [NSString stringWithFormat:@"PLAN_UNIT_%@", drawingPlanId];
[APIClient requestGetUnitPlan:^(NSDictionary *res, NSError *err) {
if (res) {
id dataArray = res[@"Data"];
NSDictionary *objData = @{};
if ([dataArray isKindOfClass:[NSArray class]] && [dataArray count] > 0) {
id firstItem = dataArray[0];
if ([firstItem isKindOfClass:[NSDictionary class]]) {
objData = firstItem;
}
}
[LocalStorage saveDictionary:objData forKey:key];
}
next();
}];
}];
[tasks addObject:^(void (^next)(void)) {
NSString *key = [NSString stringWithFormat:@"SETTINGS_%@", projectId];
[APIClient requestGeneralSettings:^(NSDictionary *res, NSError *err) {
if (res) [LocalStorage saveDictionary:res[@"Data"] forKey:key];
next();
}];
}];
[tasks addObject:^(void (^next)(void)) {
NSString *key = [NSString stringWithFormat:@"COMMONAREA_%@", projectId];
[APIClient requestCommonArea:^(NSDictionary *res, NSError *err) {
if (res) [LocalStorage saveDictionary:res[@"Data"] forKey:key];
next();
}];
}];
[tasks addObject:^(void (^next)(void)) {
[APIClient requestDefectMatrix:^(NSDictionary *res, NSError *err) {
if (!res || err) {
next();
return;
}
NSDictionary *data = res[@"Data"];
if (![data isKindOfClass:[NSDictionary class]]) {
next();
return;
}
// 1️⃣ Shared lists
NSArray *priority = data[@"priority"] ?: @[];
NSArray *contractor = data[@"contractor"] ?: @[];
NSArray *newIssueStatus = data[@"new_issue_status"] ?: @[];
NSArray *startWorkStatus = data[@"start_work_status"] ?: @[];
NSArray *subContractor = data[@"sub_contractor"] ?: @[];
NSArray *locationStatus = data[@"location_status"] ?: @[];
NSString *notMe = data[@"not_me"] ?: @"";
// 2️⃣ Defect matrix expansion
NSArray *defectMatrix = data[@"defect_matrix"];
if ([defectMatrix isKindOfClass:[NSArray class]]) {
for (NSDictionary *entry in defectMatrix) {
NSArray *locationIds = entry[@"location_id"];
if (![locationIds isKindOfClass:[NSArray class]] || locationIds.count == 0) {
continue;
}
NSArray *category = entry[@"category"] ?: @[];
NSDictionary *settings = @{
@"category": category,
@"priority": priority,
@"contractor": contractor,
@"new_issue_status": newIssueStatus,
@"start_work_status": startWorkStatus,
@"sub_contractor": subContractor,
@"location_status": locationStatus,
@"not_me": notMe
};
// 3️⃣ Save per location_id
for (id locationId in locationIds) {
NSString *locationKey =
[NSString stringWithFormat:@"LOCATION_SETTINGS_%@", locationId];
[LocalStorage saveDictionary:settings forKey:locationKey];
}
}
}
next();
}];
}];
// 3️⃣ Add image download task as the last task
[tasks addObject:^(void (^next)(void)) {
if (selectedUnits.count == 0) {
next();
return;
}
[self fetchOfflineImageListForSelectedUnits:selectedUnits completion:^(NSArray<NSURL *> *urls, NSError *error) {
if (error || urls.count == 0) {
next();
return;
}
[self downloadImagesSequentially:urls progress:^(NSInteger completedImages, NSInteger totalImages) {
// Map image progress into overall progress
NSInteger overallCompleted = 6 + completedImages;
NSInteger overallTotal = 6 + urls.count;
if (progress) progress(overallCompleted, overallTotal);
} next:^{
next();
}];
}];
}];
// 4️⃣ Sequential executor
__block NSInteger currentTaskIndex = 0;
void (^runNextTask)(void);
runNextTask = ^{
if (currentTaskIndex >= tasks.count) {
[self saveLastOfflineSyncDate];
if (completion) completion(YES);
return;
}
void (^task)(void (^next)(void)) = tasks[currentTaskIndex];
currentTaskIndex++;
// Update progress for API tasks
if (progress && currentTaskIndex <= 6) {
NSInteger overallTotal = 6 + selectedUnits.count;
progress(currentTaskIndex, overallTotal);
}
task(runNextTask);
};
runNextTask();
}
#pragma mark - Image Download Helpers
- (NSArray *)buildImageSyncPayloadFromUnits:(NSArray *)units {
NSMutableDictionary<NSString *, NSMutableArray *> *grouped = @{}.mutableCopy;
for (NSDictionary *unit in units) {
NSString *projectId = unit[@"project_id"];
NSString *planId = unit[@"plan_id"];
if (!projectId || !planId) continue;
if (!grouped[projectId]) grouped[projectId] = [NSMutableArray array];
[grouped[projectId] addObject:planId];
}
NSMutableArray *result = [NSMutableArray array];
for (NSString *projectId in grouped) {
[result addObject:@{ @"project_id": projectId, @"plan_id": grouped[projectId] }];
}
return result;
}
- (void)fetchOfflineImageListForSelectedUnits:(NSArray *)units
completion:(void (^)(NSArray<NSURL *> *, NSError *))completion {
NSArray *payload = [self buildImageSyncPayloadFromUnits:units];
if (payload.count == 0) {
completion(@[], nil);
return;
}
[APIClient requestArrayImage:payload completion:^(NSDictionary *res, NSError *err) {
if (err) {
completion(nil, err);
return;
}
NSMutableOrderedSet<NSURL *> *urls = [NSMutableOrderedSet orderedSet];
NSDictionary *fileImage = res[@"Data"][@"file_image"];
for (NSDictionary *img in fileImage[@"plan_image"]) {
if (img[@"link"]) [urls addObject:[NSURL URLWithString:img[@"link"]]];
}
for (NSDictionary *img in fileImage[@"issue_image"]) {
if (img[@"link"]) [urls addObject:[NSURL URLWithString:img[@"link"]]];
}
completion(urls.array, nil);
}];
}
- (void)downloadImagesSequentially:(NSArray<NSURL *> *)urls
progress:(void (^)(NSInteger completed, NSInteger total))progress
next:(void (^)(void))next {
__block NSInteger index = 0;
NSInteger total = urls.count;
if (total == 0) {
if (next) next();
return;
}
__block void (^downloadNext)(void);
__weak __block void (^weakDownloadNext)(void);
downloadNext = ^{
if (index >= total) {
if (next) next();
return;
}
NSURL *url = urls[index];
[APIClient downloadAndCacheImage:url completion:^(BOOL success) {
index++;
if (progress) progress(index, total);
if (weakDownloadNext) weakDownloadNext();
}];
};
weakDownloadNext = downloadNext;
downloadNext();
}
- (void)saveLastOfflineSyncDate {
NSDate *now = [NSDate date];
NSTimeInterval timestamp = [now timeIntervalSince1970];
[LocalStorage saveObject:@(timestamp)
forKey:kLastOfflineSyncDateKey];
}
@end @end
...@@ -8,32 +8,32 @@ ...@@ -8,32 +8,32 @@
<key>BinaryPath</key> <key>BinaryPath</key>
<string>QmsPluginFramework.framework/QmsPluginFramework</string> <string>QmsPluginFramework.framework/QmsPluginFramework</string>
<key>LibraryIdentifier</key> <key>LibraryIdentifier</key>
<string>ios-arm64_x86_64-simulator</string> <string>ios-arm64</string>
<key>LibraryPath</key> <key>LibraryPath</key>
<string>QmsPluginFramework.framework</string> <string>QmsPluginFramework.framework</string>
<key>SupportedArchitectures</key> <key>SupportedArchitectures</key>
<array> <array>
<string>arm64</string> <string>arm64</string>
<string>x86_64</string>
</array> </array>
<key>SupportedPlatform</key> <key>SupportedPlatform</key>
<string>ios</string> <string>ios</string>
<key>SupportedPlatformVariant</key>
<string>simulator</string>
</dict> </dict>
<dict> <dict>
<key>BinaryPath</key> <key>BinaryPath</key>
<string>QmsPluginFramework.framework/QmsPluginFramework</string> <string>QmsPluginFramework.framework/QmsPluginFramework</string>
<key>LibraryIdentifier</key> <key>LibraryIdentifier</key>
<string>ios-arm64</string> <string>ios-arm64_x86_64-simulator</string>
<key>LibraryPath</key> <key>LibraryPath</key>
<string>QmsPluginFramework.framework</string> <string>QmsPluginFramework.framework</string>
<key>SupportedArchitectures</key> <key>SupportedArchitectures</key>
<array> <array>
<string>arm64</string> <string>arm64</string>
<string>x86_64</string>
</array> </array>
<key>SupportedPlatform</key> <key>SupportedPlatform</key>
<string>ios</string> <string>ios</string>
<key>SupportedPlatformVariant</key>
<string>simulator</string>
</dict> </dict>
</array> </array>
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment