Commit 8eaaf67d authored by Wei Han's avatar Wei Han

code update

parent 9eb256b5
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
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 */; };
F2581A09DD01A13570F68628 /* libPods-example.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2D30A3A8F21F213E96F2C8F7 /* libPods-example.a */; }; FB2FBB7BA35D68426456E2DE /* libPods-example.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8A86FB22DA3710B6C48332E2 /* libPods-example.a */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
...@@ -19,11 +19,11 @@ ...@@ -19,11 +19,11 @@
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = example/Images.xcassets; sourceTree = "<group>"; }; 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = example/Images.xcassets; sourceTree = "<group>"; };
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = example/Info.plist; sourceTree = "<group>"; }; 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = example/Info.plist; sourceTree = "<group>"; };
13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = PrivacyInfo.xcprivacy; path = example/PrivacyInfo.xcprivacy; sourceTree = "<group>"; }; 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = PrivacyInfo.xcprivacy; path = example/PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
2D30A3A8F21F213E96F2C8F7 /* libPods-example.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-example.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 502E3324911D1E5D0C0EFA69 /* 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>"; };
508BA7CC4A1A0B5A23D84649 /* Pods-example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-example.release.xcconfig"; path = "Target Support Files/Pods-example/Pods-example.release.xcconfig"; sourceTree = "<group>"; };
761780EC2CA45674006654EE /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = example/AppDelegate.swift; sourceTree = "<group>"; }; 761780EC2CA45674006654EE /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = example/AppDelegate.swift; sourceTree = "<group>"; };
7C33988A67E3A60B09B56690 /* 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>"; };
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>"; };
8A86FB22DA3710B6C48332E2 /* libPods-example.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-example.a"; sourceTree = BUILT_PRODUCTS_DIR; };
90821031277E35B5DA493B2D /* 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; };
/* End PBXFileReference section */ /* End PBXFileReference section */
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
F2581A09DD01A13570F68628 /* libPods-example.a in Frameworks */, FB2FBB7BA35D68426456E2DE /* 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 */,
2D30A3A8F21F213E96F2C8F7 /* libPods-example.a */, 8A86FB22DA3710B6C48332E2 /* 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 = (
7C33988A67E3A60B09B56690 /* Pods-example.debug.xcconfig */, 502E3324911D1E5D0C0EFA69 /* Pods-example.debug.xcconfig */,
508BA7CC4A1A0B5A23D84649 /* Pods-example.release.xcconfig */, 90821031277E35B5DA493B2D /* 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 = (
D47CB7885794FE215FE8B238 /* [CP] Check Pods Manifest.lock */, 9AD4CB19B4D3412BF066BD3E /* [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 */,
ABA4014BAFCE28774B9D5225 /* [CP] Embed Pods Frameworks */, 9B7F90516C6E40D72BFE9346 /* [CP] Embed Pods Frameworks */,
4B920070F73F4233D4D79AC3 /* [CP] Copy Pods Resources */, DD2A8D0D8F75B0C93112FC8D /* [CP] Copy Pods Resources */,
); );
buildRules = ( buildRules = (
); );
...@@ -183,24 +183,29 @@ ...@@ -183,24 +183,29 @@
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";
}; };
4B920070F73F4233D4D79AC3 /* [CP] Copy Pods Resources */ = { 9AD4CB19B4D3412BF066BD3E /* [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-resources-${CONFIGURATION}-input-files.xcfilelist",
); );
name = "[CP] Copy Pods Resources"; 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-resources-${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-resources.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;
}; };
ABA4014BAFCE28774B9D5225 /* [CP] Embed Pods Frameworks */ = { 9B7F90516C6E40D72BFE9346 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
...@@ -217,26 +222,21 @@ ...@@ -217,26 +222,21 @@
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-frameworks.sh\"\n"; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-frameworks.sh\"\n";
showEnvVarsInLog = 0; showEnvVarsInLog = 0;
}; };
D47CB7885794FE215FE8B238 /* [CP] Check Pods Manifest.lock */ = { DD2A8D0D8F75B0C93112FC8D /* [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;
}; };
/* 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 = 7C33988A67E3A60B09B56690 /* Pods-example.debug.xcconfig */; baseConfigurationReference = 502E3324911D1E5D0C0EFA69 /* 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 = 508BA7CC4A1A0B5A23D84649 /* Pods-example.release.xcconfig */; baseConfigurationReference = 90821031277E35B5DA493B2D /* 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>
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#import "AppointmentViewMoreViewController.h" #import "AppointmentViewMoreViewController.h"
#import "GeneralInfoViewController.h" #import "GeneralInfoViewController.h"
#import "AccessItemViewController.h" #import "AccessItemViewController.h"
#import "OfflineSyncManager.h"
@interface DashboardViewController () @interface DashboardViewController ()
...@@ -78,6 +79,9 @@ ...@@ -78,6 +79,9 @@
[self checkAndRefreshAPIs]; [self checkAndRefreshAPIs];
NSLog(@"in viewDidLoad ClientID: %@, ClientCode: %@, user_token: %@", self.ClientID, self.ClientCode, self.user_token); NSLog(@"in viewDidLoad ClientID: %@, ClientCode: %@, user_token: %@", self.ClientID, self.ClientCode, self.user_token);
NSLog(@"dashboard.drawerController = %@", self.drawerController); NSLog(@"dashboard.drawerController = %@", self.drawerController);
// [[OfflineSyncManager shared] clearOfflineQueue];
} }
- (void)viewWillAppear:(BOOL)animated { - (void)viewWillAppear:(BOOL)animated {
...@@ -1132,6 +1136,8 @@ ...@@ -1132,6 +1136,8 @@
IssueDetailViewController *nextVC = [[IssueDetailViewController alloc] init]; IssueDetailViewController *nextVC = [[IssueDetailViewController alloc] init];
nextVC.modalPresentationStyle = UIModalPresentationFullScreen; nextVC.modalPresentationStyle = UIModalPresentationFullScreen;
nextVC.issueData = issueData; nextVC.issueData = issueData;
nextVC.projectCode = self.projectCode;
nextVC.projectName = self.projectName;
[self presentViewController:nextVC animated:YES completion:nil]; [self presentViewController:nextVC animated:YES completion:nil];
} else { } else {
NSLog(@"⚠️ No valid issue data found for tapped card: %@", issueData); NSLog(@"⚠️ No valid issue data found for tapped card: %@", issueData);
...@@ -1182,6 +1188,8 @@ ...@@ -1182,6 +1188,8 @@
-(void)issueHeaderTapped:(UITapGestureRecognizer *)gesture { -(void)issueHeaderTapped:(UITapGestureRecognizer *)gesture {
IssuesViewController *nextVC = [[IssuesViewController alloc] init]; IssuesViewController *nextVC = [[IssuesViewController alloc] init];
nextVC.modalPresentationStyle = UIModalPresentationFullScreen; nextVC.modalPresentationStyle = UIModalPresentationFullScreen;
nextVC.projectCode = self.projectCode;
nextVC.projectName = self.projectName;
[self presentViewController:nextVC animated:YES completion:nil]; [self presentViewController:nextVC animated:YES completion:nil];
} }
......
...@@ -10,6 +10,9 @@ NS_ASSUME_NONNULL_BEGIN ...@@ -10,6 +10,9 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, strong) NSString *status; @property (nonatomic, strong) NSString *status;
@property (nonatomic, strong) NSString *unitName; @property (nonatomic, strong) NSString *unitName;
@property (nonatomic, strong) NSString *reference; @property (nonatomic, strong) NSString *reference;
@property (nonatomic, strong) NSString *userName;
@property (nonatomic, strong) NSString *projectName;
@property (nonatomic, strong) NSString *projectCode;
@end @end
......
...@@ -565,14 +565,32 @@ didFinishPickingMediaWithInfo:(NSDictionary<UIImagePickerControllerInfoKey,id> * ...@@ -565,14 +565,32 @@ didFinishPickingMediaWithInfo:(NSDictionary<UIImagePickerControllerInfoKey,id> *
} }
if (![NetworkManager.sharedManager isConnected]) { if (![NetworkManager.sharedManager isConnected]) {
NSDictionary *displayData = @{
@"projectName": self.projectName ?: @"Unknown Project",
@"unitName": self.projectCode ?: @"-",
@"createdBy": @"Athirah Zaidi",
@"createdAt": [self todayDateString],
@"issueDisplay": @"Add Info",
};
NSLog(@"add info displayData %@", displayData);
[[OfflineSyncManager shared] queueAddInfo:self.issueID [[OfflineSyncManager shared] queueAddInfo:self.issueID
remarks:self.commentTextView.text remarks:self.commentTextView.text
displayInfo:displayData
images:images images:images
mediaURL:self.selectedMediaURL mediaURL:self.selectedMediaURL
mediaType:mediaType]; mediaType:mediaType];
[self showAlertWithTitle:@"Saved Offline" [self showAlertWithTitle:@"Saved Offline"
message:@"This will be synced when you're back online."]; message:@"This will be synced when you're back online."];
UIViewController *presenter = self.presentingViewController;
if (presenter.presentingViewController) {
[presenter.presentingViewController dismissViewControllerAnimated:YES completion:nil];
return; return;
} else {
[self dismissViewControllerAnimated:YES completion:nil];
return;
}
} }
if (!hasImages && !hasMedia) { if (!hasImages && !hasMedia) {
...@@ -669,4 +687,11 @@ didFinishPickingMediaWithInfo:(NSDictionary<UIImagePickerControllerInfoKey,id> * ...@@ -669,4 +687,11 @@ didFinishPickingMediaWithInfo:(NSDictionary<UIImagePickerControllerInfoKey,id> *
return valid; return valid;
} }
- (NSString *)todayDateString {
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.locale = [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"];
formatter.dateFormat = @"yyyy-MM-dd";
return [formatter stringFromDate:[NSDate date]];
}
@end @end
...@@ -916,7 +916,17 @@ didFinishPickingMediaWithInfo:(NSDictionary<UIImagePickerControllerInfoKey,id> * ...@@ -916,7 +916,17 @@ didFinishPickingMediaWithInfo:(NSDictionary<UIImagePickerControllerInfoKey,id> *
NSLog(@"📦 Sending UpdateIssue with data: %@", updateData); NSLog(@"📦 Sending UpdateIssue with data: %@", updateData);
if (![NetworkManager.sharedManager isConnected]) { if (![NetworkManager.sharedManager isConnected]) {
NSDictionary *displayData = @{
@"projectName": self.projectName ?: @"Unknown Project",
@"unitName": self.projectCode ?: @"-",
@"createdBy": @"Athirah Zaidi",
@"createdAt": [self todayDateString],
@"issueDisplay": @"Edit Issue",
};
NSLog(@"edit issues displayData %@", displayData);
[[OfflineSyncManager shared] queueUpdateIssue:updateData [[OfflineSyncManager shared] queueUpdateIssue:updateData
display:displayData
images:self.addedImages]; images:self.addedImages];
[self showAlertWithTitle:@"Saved Offline" [self showAlertWithTitle:@"Saved Offline"
message:@"This will be synced when you're back online."]; message:@"This will be synced when you're back online."];
...@@ -924,8 +934,10 @@ didFinishPickingMediaWithInfo:(NSDictionary<UIImagePickerControllerInfoKey,id> * ...@@ -924,8 +934,10 @@ didFinishPickingMediaWithInfo:(NSDictionary<UIImagePickerControllerInfoKey,id> *
UIViewController *presenter = self.presentingViewController; UIViewController *presenter = self.presentingViewController;
if (presenter.presentingViewController) { if (presenter.presentingViewController) {
[presenter.presentingViewController dismissViewControllerAnimated:YES completion:nil]; [presenter.presentingViewController dismissViewControllerAnimated:YES completion:nil];
return;
} else { } else {
[self dismissViewControllerAnimated:YES completion:nil]; [self dismissViewControllerAnimated:YES completion:nil];
return;
} }
} }
...@@ -988,10 +1000,18 @@ didFinishPickingMediaWithInfo:(NSDictionary<UIImagePickerControllerInfoKey,id> * ...@@ -988,10 +1000,18 @@ didFinishPickingMediaWithInfo:(NSDictionary<UIImagePickerControllerInfoKey,id> *
@"os": @"AND" @"os": @"AND"
}; };
NSLog(@"NetworkManager.sharedManager isConnected = %@",
[NetworkManager.sharedManager isConnected] ? @"YES" : @"NO");
if (![NetworkManager.sharedManager isConnected]) { if (![NetworkManager.sharedManager isConnected]) {
NSDictionary *displayData = @{
@"projectName": self.projectName ?: @"Unknown Project",
@"unitName": self.projectCode ?: @"-",
@"createdBy": @"Athirah Zaidi",
@"createdAt": [self todayDateString],
@"issueDisplay": @"Add Issue",
};
NSLog(@"add issue displayData %@", displayData);
[[OfflineSyncManager shared] queueAddIssue:issueData [[OfflineSyncManager shared] queueAddIssue:issueData
display:displayData
images:self.uploadedImages]; images:self.uploadedImages];
[self showAlertWithTitle:@"Saved Offline" [self showAlertWithTitle:@"Saved Offline"
message:@"This will be synced when you're back online."]; message:@"This will be synced when you're back online."];
...@@ -999,8 +1019,10 @@ didFinishPickingMediaWithInfo:(NSDictionary<UIImagePickerControllerInfoKey,id> * ...@@ -999,8 +1019,10 @@ didFinishPickingMediaWithInfo:(NSDictionary<UIImagePickerControllerInfoKey,id> *
UIViewController *presenter = self.presentingViewController; UIViewController *presenter = self.presentingViewController;
if (presenter.presentingViewController) { if (presenter.presentingViewController) {
[presenter.presentingViewController dismissViewControllerAnimated:YES completion:nil]; [presenter.presentingViewController dismissViewControllerAnimated:YES completion:nil];
return;
} else { } else {
[self dismissViewControllerAnimated:YES completion:nil]; [self dismissViewControllerAnimated:YES completion:nil];
return;
} }
} }
...@@ -1095,4 +1117,11 @@ didFinishPickingMediaWithInfo:(NSDictionary<UIImagePickerControllerInfoKey,id> * ...@@ -1095,4 +1117,11 @@ didFinishPickingMediaWithInfo:(NSDictionary<UIImagePickerControllerInfoKey,id> *
self.scrollView.scrollIndicatorInsets = UIEdgeInsetsZero; self.scrollView.scrollIndicatorInsets = UIEdgeInsetsZero;
} }
- (NSString *)todayDateString {
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.locale = [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"];
formatter.dateFormat = @"yyyy-MM-dd";
return [formatter stringFromDate:[NSDate date]];
}
@end @end
...@@ -770,7 +770,7 @@ ...@@ -770,7 +770,7 @@
nextVC.locationData = mainData; nextVC.locationData = mainData;
nextVC.defectMatrix = defectMatrix; nextVC.defectMatrix = defectMatrix;
nextVC.projectCode = self.projectCode ?: @""; nextVC.projectCode = self.projectCode ?: @"";
nextVC.projectName = self.unitName; nextVC.projectName = self.projectName;
nextVC.planX = self.tappedPlanX; nextVC.planX = self.tappedPlanX;
nextVC.planY = self.tappedPlanY; nextVC.planY = self.tappedPlanY;
......
...@@ -11,6 +11,8 @@ NS_ASSUME_NONNULL_BEGIN ...@@ -11,6 +11,8 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, strong) NSString *status; @property (nonatomic, strong) NSString *status;
@property (nonatomic, strong) NSString *unitName; @property (nonatomic, strong) NSString *unitName;
@property (nonatomic, strong) NSString *reference; @property (nonatomic, strong) NSString *reference;
@property (nonatomic, strong) NSString *projectName;
@property (nonatomic, strong) NSString *projectCode;
@end @end
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
@property (nonatomic, strong) NSArray *historyItems; @property (nonatomic, strong) NSArray *historyItems;
@property (nonatomic, strong) NSArray *sortedDates; @property (nonatomic, strong) NSArray *sortedDates;
@property (nonatomic, strong) NSDictionary *groupedHistory; @property (nonatomic, strong) NSDictionary *groupedHistory;
@property (nonatomic, strong) NSString *userName;
@end @end
...@@ -48,9 +49,9 @@ ...@@ -48,9 +49,9 @@
[self setupTableView]; [self setupTableView];
[self handleIssues]; [self handleIssues];
NSLog(@"📦 Data received by History:\n issueID: %@\n planID: %@\n status: %@\n unitName: %@\n reference: %@", NSLog(@"📦 Data received by History:\n projectName: %@\n projectCode: %@\n status: %@\n unitName: %@\n reference: %@",
self.issueID, self.projectName,
self.planID, self.projectCode,
self.status, self.status,
self.unitName, self.unitName,
self.reference); self.reference);
...@@ -346,7 +347,7 @@ ...@@ -346,7 +347,7 @@
UIScrollView *thumbScroll = [cell viewWithTag:105]; UIScrollView *thumbScroll = [cell viewWithTag:105];
NSDictionary *user = item[@"user"]; NSDictionary *user = item[@"user"];
NSString *userName = [self safeString:user[@"name"]]; self.userName = [self safeString:user[@"name"]];
NSString *remark = [self safeString:item[@"remarks"]]; NSString *remark = [self safeString:item[@"remarks"]];
NSString *status = [self safeString:item[@"status_external"]]; NSString *status = [self safeString:item[@"status_external"]];
NSString *colorHex = [self safeString:item[@"status_ex_color"]]; NSString *colorHex = [self safeString:item[@"status_ex_color"]];
...@@ -362,7 +363,7 @@ ...@@ -362,7 +363,7 @@
} }
timeLabel.text = timePart.length > 0 ? timePart : @"--"; timeLabel.text = timePart.length > 0 ? timePart : @"--";
userLabel.text = userName; userLabel.text = self.userName;
remarkLabel.text = remark; remarkLabel.text = remark;
dateLabel.text = datePost; dateLabel.text = datePost;
statusLabel.text = status; statusLabel.text = status;
...@@ -449,6 +450,9 @@ ...@@ -449,6 +450,9 @@
vc.status = self.status; vc.status = self.status;
vc.unitName = self.unitName; vc.unitName = self.unitName;
vc.reference = self.reference; vc.reference = self.reference;
vc.userName = self.userName;
vc.projectCode = self.projectCode;
vc.projectName = self.projectName;
vc.modalPresentationStyle = UIModalPresentationFullScreen; vc.modalPresentationStyle = UIModalPresentationFullScreen;
[self presentViewController:vc animated:YES completion:nil]; [self presentViewController:vc animated:YES completion:nil];
} }
......
...@@ -7,6 +7,8 @@ NS_ASSUME_NONNULL_BEGIN ...@@ -7,6 +7,8 @@ NS_ASSUME_NONNULL_BEGIN
@interface IssueDetailViewController : UIViewController @interface IssueDetailViewController : UIViewController
@property (nonatomic, strong) NSDictionary *issueData; @property (nonatomic, strong) NSDictionary *issueData;
@property (nonatomic, strong) NSString *projectName;
@property (nonatomic, strong) NSString *projectCode;
@end @end
......
...@@ -25,19 +25,8 @@ ...@@ -25,19 +25,8 @@
self.view.backgroundColor = [UIColor systemGray6Color]; self.view.backgroundColor = [UIColor systemGray6Color];
self.title = @"Issue Details"; self.title = @"Issue Details";
NSLog(@"📦 Received Issue Data: %@", self.issueData); NSLog(@"📦 navigated to issueDetailViewController with:\n self.projectName=%@\n self.projectCode=%@",
NSString *text = [NSString stringWithFormat:@"%@", self.issueData]; self.projectCode, self.projectName);
NSString *docsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
NSString *filePath = [docsPath stringByAppendingPathComponent:@"issueData.txt"];
// Convert issueData to a readable string
NSString *plainText = [NSString stringWithFormat:@"%@", self.issueData];
// Write as plain UTF-8 text
[plainText writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:nil];
// Log the path so you can find it
NSLog(@"📁 issueData saved as plain text to: %@", filePath);
[self setupHeader]; [self setupHeader];
[self setupScrollView]; [self setupScrollView];
...@@ -559,10 +548,12 @@ ...@@ -559,10 +548,12 @@
nextVC.status = self.issueData[@"status_external"]; nextVC.status = self.issueData[@"status_external"];
nextVC.unitName = self.issueData[@"plan_unit"]; nextVC.unitName = self.issueData[@"plan_unit"];
nextVC.reference = self.issueData[@"reference"] ?: self.issueData[@"issue_reference"]; nextVC.reference = self.issueData[@"reference"] ?: self.issueData[@"issue_reference"];
nextVC.projectCode = self.projectCode;
nextVC.projectName = self.projectName;
NSLog(@"📦 Data being sent to History:\n issueID: %@\n planID: %@\n status: %@\n unitName: %@\n reference: %@", NSLog(@"📦 Data being sent to History:\n issueID: %@\n planID: %@\n status: %@\n unitName: %@\n reference: %@",
nextVC.issueID, nextVC.projectCode,
nextVC.planID, nextVC.projectName,
nextVC.status, nextVC.status,
nextVC.unitName, nextVC.unitName,
nextVC.reference); nextVC.reference);
...@@ -623,9 +614,11 @@ ...@@ -623,9 +614,11 @@
planVC.locationId = strLocationID; planVC.locationId = strLocationID;
planVC.issueContent = issueContent; planVC.issueContent = issueContent;
planVC.issueFullData = data; planVC.issueFullData = data;
planVC.projectCode = self.projectCode;
planVC.projectName = self.projectName;
NSLog(@"🧭 Navigating to PlanScreen with:\n type=%@\n unitID=%@\n unitName=%@\n location= %@ & %@\n issue_id=%@\n", NSLog(@"🧭 Navigating to PlanScreen with:\n projectCode=%@\n projectName=%@\n unitName=%@\n location= %@ & %@\n issue_id=%@\n",
planVC.type, planVC.unitID, planVC.unitName, planVC.locationName, planVC.locationId, planVC.issueContent[@"issue_id"]); planVC.projectCode, planVC.projectName, planVC.unitName, planVC.locationName, planVC.locationId, planVC.issueContent[@"issue_id"]);
[self presentViewController:planVC animated:YES completion:nil]; [self presentViewController:planVC animated:YES completion:nil];
} }
......
...@@ -6,6 +6,9 @@ NS_ASSUME_NONNULL_BEGIN ...@@ -6,6 +6,9 @@ NS_ASSUME_NONNULL_BEGIN
@interface IssuesViewController : UIViewController @interface IssuesViewController : UIViewController
@property (nonatomic, strong) NSString *projectCode;
@property (nonatomic, strong) NSString *projectName;
@end @end
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END
...@@ -720,7 +720,8 @@ ...@@ -720,7 +720,8 @@
IssueDetailViewController *detailVC = [[IssueDetailViewController alloc] init]; IssueDetailViewController *detailVC = [[IssueDetailViewController alloc] init];
detailVC.modalPresentationStyle = UIModalPresentationFullScreen; detailVC.modalPresentationStyle = UIModalPresentationFullScreen;
detailVC.issueData = issue; // Pass the selected data detailVC.issueData = issue; // Pass the selected data
detailVC.projectCode = self.projectCode;
detailVC.projectName = self.projectName;
NSLog(@"🧭 Navigating to IssueDetailsViewController with issue: %@", issue); NSLog(@"🧭 Navigating to IssueDetailsViewController with issue: %@", issue);
[self presentViewController:detailVC animated:YES completion:nil]; [self presentViewController:detailVC animated:YES completion:nil];
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
#import "SyncPendingViewController.h" #import "SyncPendingViewController.h"
#import "PendingDetailsViewController.h" #import "PendingDetailsViewController.h"
#import "OfflineSyncManager.h"
typedef NS_ENUM(NSInteger, SyncPendingViewMode) { typedef NS_ENUM(NSInteger, SyncPendingViewMode) {
SyncPendingViewModeList, SyncPendingViewModeList,
...@@ -154,9 +155,20 @@ typedef NS_ENUM(NSInteger, SyncPendingViewMode) { ...@@ -154,9 +155,20 @@ typedef NS_ENUM(NSInteger, SyncPendingViewMode) {
self.viewMode = SyncPendingViewModeList; self.viewMode = SyncPendingViewModeList;
self.view.backgroundColor = [UIColor systemGroupedBackgroundColor]; self.view.backgroundColor = [UIColor systemGroupedBackgroundColor];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(loadPendingData)
name:@"OfflineSyncUpdated"
object:nil];
[self setupHeader]; [self setupHeader];
[self setupTableView]; [self setupTableView];
[self setupDummyData]; [self setupDummyData];
[self loadPendingData];
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
} }
#pragma mark - Header #pragma mark - Header
...@@ -240,7 +252,23 @@ typedef NS_ENUM(NSInteger, SyncPendingViewMode) { ...@@ -240,7 +252,23 @@ typedef NS_ENUM(NSInteger, SyncPendingViewMode) {
]]; ]];
} }
//Dummy Data #pragma mark - Data
- (void)loadPendingData {
NSArray *pending =
[[OfflineSyncManager shared] pendingItemsForUI];
NSLog(@"🖥 [SyncPendingVC] Pending sections: %@", pending);
if (pending.count > 0) {
self.sections = pending;
} else {
[self setupDummyData];
}
[self reloadUI];
}
- (void)setupDummyData { - (void)setupDummyData {
self.sections = @[ self.sections = @[
@{ @{
...@@ -304,10 +332,8 @@ didSelectRowAtIndexPath:(NSIndexPath *)indexPath { ...@@ -304,10 +332,8 @@ didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSDictionary *item = NSDictionary *item =
self.sections[indexPath.section][@"items"][indexPath.row]; self.sections[indexPath.section][@"items"][indexPath.row];
self.selectedItem = item; NSDictionary *raw = item[@"raw"] ?: item;
self.viewMode = SyncPendingViewModeDetails; [self showDetailsForItem:raw];
[self showDetailsForItem:item];
} }
- (CGFloat)tableView:(UITableView *)tableView - (CGFloat)tableView:(UITableView *)tableView
...@@ -355,6 +381,7 @@ heightForHeaderInSection:(NSInteger)section { ...@@ -355,6 +381,7 @@ heightForHeaderInSection:(NSInteger)section {
NSLog(@"Share triggered (stub)"); NSLog(@"Share triggered (stub)");
}]]; }]];
[alert addAction:[UIAlertAction actionWithTitle:@"Cancel" [alert addAction:[UIAlertAction actionWithTitle:@"Cancel"
style:UIAlertActionStyleCancel style:UIAlertActionStyleCancel
handler:nil]]; handler:nil]];
......
// SyncProjectViewController.mm // SyncProjectViewController.mm
#import "SyncProjectViewController.h" #import "SyncProjectViewController.h"
#import "LocalStorage.h" #import "LocalStorage.h"
#import "APIClient.h"
#import "NetworkManager.h"
#import "OfflineSyncManager.h"
typedef NS_ENUM(NSInteger, SyncProjectScreenState) { typedef NS_ENUM(NSInteger, SyncProjectScreenState) {
SyncProjectScreenStateOfflineUnit, SyncProjectScreenStateOfflineUnit,
...@@ -14,6 +17,7 @@ static NSString * const kProjectListKey = @"PROJECT_LIST"; ...@@ -14,6 +17,7 @@ static NSString * const kProjectListKey = @"PROJECT_LIST";
@interface SyncUnit : NSObject @interface SyncUnit : NSObject
@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) NSString *projectId; @property (nonatomic, strong) NSString *projectId;
@property (nonatomic, strong) NSString *projectName; @property (nonatomic, strong) NSString *projectName;
@property (nonatomic, assign) BOOL selected; @property (nonatomic, assign) BOOL selected;
...@@ -60,13 +64,14 @@ UICollectionViewDataSource ...@@ -60,13 +64,14 @@ UICollectionViewDataSource
@property (nonatomic, assign) BOOL hasChanges; @property (nonatomic, assign) BOOL hasChanges;
@property (nonatomic, strong) NSMutableArray<SyncUnit *> *selectedUnits; @property (nonatomic, strong) NSMutableArray<SyncUnit *> *selectedUnits;
@property (nonatomic, strong) NSMutableArray<SyncProject *> *dummyProjects;
@property (nonatomic, strong) NSMutableArray<SyncUnit *> *dummyUnits;
@property (nonatomic, strong) UITableView *offlineTableView; @property (nonatomic, strong) UITableView *offlineTableView;
@property (nonatomic, strong) UITableView *unitTableView; @property (nonatomic, strong) UITableView *unitTableView;
@property (nonatomic, strong) UICollectionView *projectCollectionView; @property (nonatomic, strong) UICollectionView *projectCollectionView;
@property (nonatomic, strong) NSMutableArray<SyncProject *> *projects;
@property (nonatomic, assign) BOOL isLoadingProjects;
@property (nonatomic, strong) SyncProject *currentProject;
@end @end
...@@ -74,11 +79,19 @@ UICollectionViewDataSource ...@@ -74,11 +79,19 @@ UICollectionViewDataSource
- (void)viewDidLoad { - (void)viewDidLoad {
[super viewDidLoad]; [super viewDidLoad];
self.view.backgroundColor = [UIColor systemGroupedBackgroundColor]; self.projects = [NSMutableArray array];
self.selectedUnitCount = 0;
self.selectedUnits = [NSMutableArray array]; self.selectedUnits = [NSMutableArray array];
self.loadingView =
[[UIActivityIndicatorView alloc]
initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleLarge];
self.loadingView.center = self.view.center;
self.loadingView.hidesWhenStopped = YES;
[self.view addSubview:self.loadingView];
[self loadSelectedUnits]; [self loadSelectedUnits];
[self setupDummyData]; [self loadProjectList];
[self setupHeader]; [self setupHeader];
[self setupContentContainers]; [self setupContentContainers];
...@@ -86,7 +99,6 @@ UICollectionViewDataSource ...@@ -86,7 +99,6 @@ UICollectionViewDataSource
[self setupSelectProjectView]; [self setupSelectProjectView];
[self setupSelectUnitView]; [self setupSelectUnitView];
[self showOfflineUnit];
[self setScreenState:SyncProjectScreenStateOfflineUnit]; [self setScreenState:SyncProjectScreenStateOfflineUnit];
} }
...@@ -323,13 +335,13 @@ UICollectionViewDataSource ...@@ -323,13 +335,13 @@ UICollectionViewDataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (tableView == self.offlineTableView) { if (tableView == self.offlineTableView) {
return self.dummyUnits.count; return self.selectedUnits.count;
} }
if (tableView == self.unitTableView) { if (tableView == self.unitTableView) {
return self.dummyUnits.count; NSLog(@"Units count = %lu", (unsigned long)self.currentProject.units.count);
return self.currentProject.units.count;
} }
return 0; return 0;
} }
...@@ -346,7 +358,14 @@ UICollectionViewDataSource ...@@ -346,7 +358,14 @@ UICollectionViewDataSource
forIndexPath:indexPath]; forIndexPath:indexPath];
} }
SyncUnit *unit = self.dummyUnits[indexPath.row]; SyncUnit *unit;
if (tableView == self.offlineTableView) {
unit = self.selectedUnits[indexPath.row];
} else {
unit = self.currentProject.units[indexPath.row];
}
cell.textLabel.text = unit.displayName; cell.textLabel.text = unit.displayName;
cell.accessoryType = unit.selected cell.accessoryType = unit.selected
? UITableViewCellAccessoryCheckmark ? UITableViewCellAccessoryCheckmark
...@@ -360,18 +379,23 @@ UICollectionViewDataSource ...@@ -360,18 +379,23 @@ UICollectionViewDataSource
- (void)tableView:(UITableView *)tableView - (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath { didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
SyncUnit *unit = self.dummyUnits[indexPath.row]; SyncUnit *unit;
if (tableView == self.offlineTableView) {
unit = self.selectedUnits[indexPath.row];
} else {
unit = self.currentProject.units[indexPath.row];
}
[self toggleUnit:unit]; [self toggleUnit:unit];
[tableView reloadRowsAtIndexPaths:@[indexPath] [tableView reloadData];
withRowAnimation:UITableViewRowAnimationAutomatic];
} }
#pragma mark - UICollectionViewDataSource #pragma mark - UICollectionViewDataSource
- (NSInteger)collectionView:(UICollectionView *)collectionView - (NSInteger)collectionView:(UICollectionView *)collectionView
numberOfItemsInSection:(NSInteger)section { numberOfItemsInSection:(NSInteger)section {
return self.dummyProjects.count; return self.projects.count;
} }
- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView - (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
...@@ -393,12 +417,165 @@ didSelectRowAtIndexPath:(NSIndexPath *)indexPath { ...@@ -393,12 +417,165 @@ didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[cell.contentView addSubview:label]; [cell.contentView addSubview:label];
} }
SyncProject *project = self.dummyProjects[indexPath.item]; SyncProject *project = self.projects[indexPath.item];
label.text = project.projectName; label.text = project.projectName;
return cell; return cell;
} }
#pragma mark - UICollectionViewDelegate
- (void)collectionView:(UICollectionView *)collectionView
didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
self.currentProject = self.projects[indexPath.item];
[self showSelectUnit];
}
#pragma mark - Project Data
- (void)loadProjectList {
NSArray *storedProjects =
[LocalStorage loadArrayForKey:kProjectListKey];
if (storedProjects && storedProjects.count > 0) {
NSLog(@"📦 Loaded PROJECT_LIST from storage");
[self parseAndApplyProjects:storedProjects];
return;
}
NSLog(@"🌐 No cached PROJECT_LIST, fetching from API");
[self fetchProjectList];
}
- (void)fetchProjectList {
[self showLoading:YES];
[APIClient fetchProjectList:^(NSDictionary *data, NSError *error) {
// debug
NSString *plainText = [NSString stringWithFormat:@"%@", data];
NSString *docsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
NSString *filePath = [docsPath stringByAppendingPathComponent:@"projectList.txt"];
[plainText writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:nil];
NSLog(@"📁 projectList saved as plain text to: %@", filePath);
[self showLoading:NO];
if (error || !data) {
NSLog(@"❌ Project list API failed: %@", error);
return;
}
// RN parity: status_code check
if (data[@"status_code"]) {
NSLog(@"❌ API status error: %@", data[@"message"]);
return;
}
NSDictionary *appData = data[@"AppData"];
if (![appData[@"status"] isEqualToString:@"success"]) {
NSLog(@"❌ API failure: %@", appData[@"message"]);
return;
}
NSArray *projects = data[@"Data"];
if (![projects isKindOfClass:[NSArray class]]) {
NSLog(@"❌ Invalid project list format");
return;
}
// RN parity: save then reload from storage
[LocalStorage saveArray:projects forKey:kProjectListKey];
NSLog(@"✅ PROJECT_LIST saved to storage");
[self parseAndApplyProjects:projects];
}];
}
- (void)parseAndApplyProjects:(NSArray *)rawProjects {
[self.projects removeAllObjects];
for (NSDictionary *dict in rawProjects) {
SyncProject *project = [SyncProject new];
project.projectId = [dict[@"id"] description];
project.projectName = dict[@"project_name"];
NSMutableArray *units = [NSMutableArray array];
for (NSDictionary *unitDict in dict[@"unit"]) {
SyncUnit *unit = [SyncUnit new];
unit.planId = [unitDict[@"id"] description];
NSString *name = unitDict[@"name"];
if (!name || name.length == 0) {
NSMutableArray *parts = [NSMutableArray array];
if (unitDict[@"block"]) [parts addObject:unitDict[@"block"]];
if (unitDict[@"level"]) [parts addObject:unitDict[@"level"]];
if (unitDict[@"unit"]) [parts addObject:unitDict[@"unit"]];
name = [parts componentsJoinedByString:@"-"];
}
unit.displayName = name;
unit.projectId = project.projectId;
unit.projectName = project.projectName;
unit.project = @{
@"id": project.projectId,
@"project_name": project.projectName
};
unit.selected = NO;
[units addObject:unit];
}
project.units = units;
[self.projects addObject:project];
}
// Apply stored selections AFTER parsing
[self applyStoredSelections];
// Refresh UI
[self.projectCollectionView reloadData];
[self.offlineTableView reloadData];
}
- (void)applyStoredSelections {
NSArray *stored =
[LocalStorage loadArrayForKey:kSyncSelectedUnitsKey];
if (!stored) return;
for (NSDictionary *dict in stored) {
NSString *planId = dict[@"plan_id"];
NSDictionary *project = dict[@"project"];
NSString *projectId = project[@"id"];
for (SyncProject *p in self.projects) {
if (![p.projectId isEqualToString:projectId]) continue;
for (SyncUnit *u in p.units) {
if ([u.planId isEqualToString:planId]) {
u.selected = YES;
if (![self isUnitAlreadySelected:u]) {
[self.selectedUnits addObject:u];
}
break;
}
}
}
}
self.selectedUnitCount = self.selectedUnits.count;
}
#pragma mark - Screen State #pragma mark - Screen State
- (void)setScreenState:(SyncProjectScreenState)state { - (void)setScreenState:(SyncProjectScreenState)state {
_screenState = state; _screenState = state;
...@@ -449,9 +626,8 @@ didSelectRowAtIndexPath:(NSIndexPath *)indexPath { ...@@ -449,9 +626,8 @@ didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
} }
- (void)showOfflineUnit { - (void)showOfflineUnit {
[self hideAllContent]; [self setScreenState:SyncProjectScreenStateOfflineUnit];
[self setHeaderTitle:@"Offline Unit"]; [self setHeaderTitle:@"Offline Unit"];
self.offlineUnitView.hidden = NO;
} }
- (void)showSelectProject { - (void)showSelectProject {
...@@ -461,9 +637,8 @@ didSelectRowAtIndexPath:(NSIndexPath *)indexPath { ...@@ -461,9 +637,8 @@ didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
} }
- (void)showSelectUnit { - (void)showSelectUnit {
[self hideAllContent]; [self setScreenState:SyncProjectScreenStateSelectUnit];
[self setHeaderTitle:@"Add Unit"]; [self setHeaderTitle:@"Add Unit"];
self.selectUnitView.hidden = NO;
} }
- (void)onAddUnit { - (void)onAddUnit {
...@@ -471,6 +646,11 @@ didSelectRowAtIndexPath:(NSIndexPath *)indexPath { ...@@ -471,6 +646,11 @@ didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
} }
- (void)onDone { - (void)onDone {
NSLog(@"Selected units count: %lu",
(unsigned long)self.selectedUnits.count);
[LocalStorage saveArray:self.selectedUnits
forKey:@"SELECTED_OFFLINE_UNITS"];
[self showOfflineUnit]; [self showOfflineUnit];
} }
...@@ -479,17 +659,26 @@ didSelectRowAtIndexPath:(NSIndexPath *)indexPath { ...@@ -479,17 +659,26 @@ didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
} }
- (void)toggleUnit:(SyncUnit *)unit { - (void)toggleUnit:(SyncUnit *)unit {
if (unit.selected) { if (unit.selected) {
unit.selected = NO; unit.selected = NO;
[self.selectedUnits removeObject:unit];
NSPredicate *predicate =
[NSPredicate predicateWithFormat:@"planId != %@", unit.planId];
self.selectedUnits =
[[self.selectedUnits filteredArrayUsingPredicate:predicate] mutableCopy];
} else { } else {
if (![self canAddUnit]) { if (![self canAddUnit]) {
NSLog(@"only 10 units recommended"); NSLog(@"only 10 units recommended");
return; return;
} }
if (![self isUnitAlreadySelected:unit]) {
unit.selected = YES; unit.selected = YES;
[self.selectedUnits addObject:unit]; [self.selectedUnits addObject:unit];
} }
}
self.hasChanges = YES; self.hasChanges = YES;
self.selectedUnitCount = self.selectedUnits.count; self.selectedUnitCount = self.selectedUnits.count;
...@@ -547,45 +736,21 @@ didSelectRowAtIndexPath:(NSIndexPath *)indexPath { ...@@ -547,45 +736,21 @@ didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
for (NSDictionary *dict in stored) { for (NSDictionary *dict in stored) {
SyncUnit *unit = [self unitFromDictionary:dict]; SyncUnit *unit = [self unitFromDictionary:dict];
if (![self isUnitAlreadySelected:unit]) {
[self.selectedUnits addObject:unit]; [self.selectedUnits addObject:unit];
} }
}
self.selectedUnitCount = self.selectedUnits.count; self.selectedUnitCount = self.selectedUnits.count;
} }
- (void)setupDummyData { - (BOOL)isUnitAlreadySelected:(SyncUnit *)unit {
// Dummy Units for (SyncUnit *u in self.selectedUnits) {
self.dummyUnits = [NSMutableArray array]; if ([u.planId isEqualToString:unit.planId]) {
for (int i = 1; i <= 12; i++) { return YES;
SyncUnit *unit = [SyncUnit new];
unit.planId = [NSString stringWithFormat:@"P%02d", i];
unit.displayName = [NSString stringWithFormat:@"Unit %d-A", i];
unit.projectId = [NSString stringWithFormat:@"PRJ%02d", i];
unit.projectName = [NSString stringWithFormat:@"Project %d", i];
unit.selected = i <= 3; // preselect first 3 units
[self.dummyUnits addObject:unit];
}
// Dummy Projects
self.dummyProjects = [NSMutableArray array];
for (int i = 1; i <= 5; i++) {
SyncProject *project = [SyncProject new];
project.projectId = [NSString stringWithFormat:@"PRJ%02d", i];
project.projectName = [NSString stringWithFormat:@"Project %d", i];
NSMutableArray *units = [NSMutableArray array];
for (int j = 1; j <= 3; j++) {
SyncUnit *unit = [SyncUnit new];
unit.planId = [NSString stringWithFormat:@"PRJ%02d-U%d", i, j];
unit.displayName = [NSString stringWithFormat:@"Unit %d-%d", i, j];
unit.projectId = project.projectId;
unit.projectName = project.projectName;
unit.selected = NO;
[units addObject:unit];
} }
project.units = units;
[self.dummyProjects addObject:project];
} }
return NO;
} }
@end @end
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#import "SyncPendingViewController.h" #import "SyncPendingViewController.h"
#import "SyncProjectViewController.h" #import "SyncProjectViewController.h"
#import "SyncEngine.h" #import "SyncEngine.h"
#import "LocalStorage.h"
@interface SyncScreenViewController () @interface SyncScreenViewController ()
...@@ -64,7 +65,7 @@ ...@@ -64,7 +65,7 @@
self.isSyncing = NO; self.isSyncing = NO;
//placeholders //placeholders
self.pendingItemCount = 1; self.pendingItemCount = 0;
self.offlineUnitCount = 1; self.offlineUnitCount = 1;
self.historyCount = 1; self.historyCount = 1;
...@@ -75,6 +76,14 @@ ...@@ -75,6 +76,14 @@
[self refreshUI]; [self refreshUI];
} }
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
self.pendingItemCount = [self loadPendingItemCount];
self.offlineUnitCount = [self loadOfflineUnitCount];
[self refreshUI];
}
- (void)dealloc { - (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self [[NSNotificationCenter defaultCenter] removeObserver:self
...@@ -468,7 +477,7 @@ ...@@ -468,7 +477,7 @@
} }
- (void)onOfflineUnitsTapped { - (void)onOfflineUnitsTapped {
if (self.offlineUnitCount == 0 || self.isOfflineMode) return; if (self.offlineUnitCount == 0) return;
NSLog(@"Offline Units tapped"); NSLog(@"Offline Units tapped");
SyncProjectViewController *nextVC = [[SyncProjectViewController alloc] init]; SyncProjectViewController *nextVC = [[SyncProjectViewController alloc] init];
...@@ -477,7 +486,7 @@ ...@@ -477,7 +486,7 @@
} }
- (void)onPendingItemsTapped { - (void)onPendingItemsTapped {
if (self.pendingItemCount == 0 || self.isOfflineMode) return; if (self.pendingItemCount == 0) return;
NSLog(@"Pending Items tapped"); NSLog(@"Pending Items tapped");
SyncPendingViewController *nextVC = [[SyncPendingViewController alloc] init]; SyncPendingViewController *nextVC = [[SyncPendingViewController alloc] init];
...@@ -486,7 +495,7 @@ ...@@ -486,7 +495,7 @@
} }
- (void)onHistoryTapped { - (void)onHistoryTapped {
if (self.historyCount == 0 || self.isOfflineMode) return; if (self.historyCount == 0) return;
NSLog(@"History tapped"); NSLog(@"History tapped");
SyncHistoryViewController *nextVC = [[SyncHistoryViewController alloc] init]; SyncHistoryViewController *nextVC = [[SyncHistoryViewController alloc] init];
...@@ -588,4 +597,14 @@ ...@@ -588,4 +597,14 @@
[self refreshUI]; [self refreshUI];
} }
- (NSInteger)loadPendingItemCount {
NSArray *queue = [LocalStorage loadArrayForKey:@"OFFLINE_SYNC_QUEUE"];
return queue.count;
}
- (NSInteger)loadOfflineUnitCount {
NSArray *units = [LocalStorage loadArrayForKey:@"SELECTED_OFFLINE_UNITS"];
return units.count;
}
@end @end
...@@ -10,23 +10,27 @@ NS_ASSUME_NONNULL_BEGIN ...@@ -10,23 +10,27 @@ NS_ASSUME_NONNULL_BEGIN
// Queue actions // Queue actions
- (void)queueAddIssue:(NSDictionary *)payload - (void)queueAddIssue:(NSDictionary *)payload
display:(NSDictionary *)display
images:(NSArray<UIImage *> *)images; images:(NSArray<UIImage *> *)images;
- (void)queueUpdateIssue:(NSDictionary *)payload - (void)queueUpdateIssue:(NSDictionary *)payload
display:(NSDictionary *)display
images:(NSArray<UIImage *> *)images; images:(NSArray<UIImage *> *)images;
- (void)queueAddInfo:(NSString *)issueID - (void)queueAddInfo:(NSString *)issueID
remarks:(NSString *)remarks remarks:(NSString *)remarks
displayInfo:(NSDictionary *)displayInfo
images:(NSArray<UIImage *> *)images images:(NSArray<UIImage *> *)images
mediaURL:(NSURL * _Nullable)mediaURL mediaURL:(NSURL * _Nullable)mediaURL
mediaType:(NSString * _Nullable)mediaType; mediaType:(NSString * _Nullable)mediaType;
// Sync
- (void)processQueueIfOnline;
// Helpers // Helpers
- (BOOL)hasPendingItems; - (BOOL)hasPendingItems;
- (NSArray *)pendingItemsForUI;
- (void)clearOfflineQueue;
@end @end
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END
...@@ -6,6 +6,8 @@ ...@@ -6,6 +6,8 @@
static NSString * const kOfflineQueueKey = @"OFFLINE_SYNC_QUEUE"; static NSString * const kOfflineQueueKey = @"OFFLINE_SYNC_QUEUE";
static NSString * const kSelectedOfflineUnitsKey = @"SELECTED_OFFLINE_UNITS";
@implementation OfflineSyncManager @implementation OfflineSyncManager
#pragma mark - Singleton #pragma mark - Singleton
...@@ -15,32 +17,19 @@ static NSString * const kOfflineQueueKey = @"OFFLINE_SYNC_QUEUE"; ...@@ -15,32 +17,19 @@ static NSString * const kOfflineQueueKey = @"OFFLINE_SYNC_QUEUE";
static dispatch_once_t onceToken; static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{ dispatch_once(&onceToken, ^{
instance = [[OfflineSyncManager alloc] init]; instance = [[OfflineSyncManager alloc] init];
[instance setupNetworkListener];
}); });
return instance; return instance;
} }
#pragma mark - Setup #pragma mark - Setup
- (void)setupNetworkListener {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(networkStatusChanged:)
name:@"NetworkStatusChanged"
object:nil];
}
- (void)networkStatusChanged:(NSNotification *)note {
BOOL connected = [note.userInfo[@"connected"] boolValue];
if (connected) {
[self processQueueIfOnline];
}
}
#pragma mark - Queue Helpers (CRITICAL) #pragma mark - Queue Helpers (CRITICAL)
- (NSMutableArray *)safeLoadQueue { - (NSMutableArray *)safeLoadQueue {
id stored = [LocalStorage loadArrayForKey:kOfflineQueueKey]; id stored = [LocalStorage loadArrayForKey:kOfflineQueueKey];
NSLog(@"📦 [OfflineSync] Raw stored queue: %@", stored);
if (![stored isKindOfClass:[NSArray class]]) { if (![stored isKindOfClass:[NSArray class]]) {
NSLog(@"🧹 [OfflineSync] Queue corrupted — resetting"); NSLog(@"🧹 [OfflineSync] Queue corrupted — resetting");
[LocalStorage removeObjectForKey:kOfflineQueueKey]; [LocalStorage removeObjectForKey:kOfflineQueueKey];
...@@ -53,6 +42,9 @@ static NSString * const kOfflineQueueKey = @"OFFLINE_SYNC_QUEUE"; ...@@ -53,6 +42,9 @@ static NSString * const kOfflineQueueKey = @"OFFLINE_SYNC_QUEUE";
[validItems addObject:obj]; [validItems addObject:obj];
} }
} }
NSLog(@"📦 [OfflineSync] Valid queue items: %@", validItems);
return validItems; return validItems;
} }
...@@ -63,23 +55,28 @@ static NSString * const kOfflineQueueKey = @"OFFLINE_SYNC_QUEUE"; ...@@ -63,23 +55,28 @@ static NSString * const kOfflineQueueKey = @"OFFLINE_SYNC_QUEUE";
#pragma mark - Public Queue APIs #pragma mark - Public Queue APIs
- (void)queueAddIssue:(NSDictionary *)payload - (void)queueAddIssue:(NSDictionary *)payload
display:(NSDictionary *)display
images:(NSArray<UIImage *> *)images { images:(NSArray<UIImage *> *)images {
[self enqueueAction:@"ADD_ISSUE" [self enqueueAction:@"ADD_ISSUE"
payload:payload payload:payload
images:images]; images:images
displayInfo:display];
} }
- (void)queueUpdateIssue:(NSDictionary *)payload - (void)queueUpdateIssue:(NSDictionary *)payload
display:(NSDictionary *)display
images:(NSArray<UIImage *> *)images { images:(NSArray<UIImage *> *)images {
[self enqueueAction:@"UPDATE_ISSUE" [self enqueueAction:@"UPDATE_ISSUE"
payload:payload payload:payload
images:images]; images:images
displayInfo:display];
} }
- (void)queueAddInfo:(NSString *)issueID - (void)queueAddInfo:(NSString *)issueID
remarks:(NSString *)remarks remarks:(NSString *)remarks
displayInfo:(NSDictionary *)displayInfo
images:(NSArray<UIImage *> *)images images:(NSArray<UIImage *> *)images
mediaURL:(NSURL * _Nullable)mediaURL mediaURL:(NSURL * _Nullable)mediaURL
mediaType:(NSString * _Nullable)mediaType { mediaType:(NSString * _Nullable)mediaType {
...@@ -92,14 +89,25 @@ static NSString * const kOfflineQueueKey = @"OFFLINE_SYNC_QUEUE"; ...@@ -92,14 +89,25 @@ static NSString * const kOfflineQueueKey = @"OFFLINE_SYNC_QUEUE";
NSMutableDictionary *item = [NSMutableDictionary dictionary]; NSMutableDictionary *item = [NSMutableDictionary dictionary];
item[@"id"] = NSUUID.UUID.UUIDString; item[@"id"] = NSUUID.UUID.UUIDString;
item[@"type"] = @"ADD_INFO"; item[@"type"] = @"ADD_INFO";
// 🔥 API payload
item[@"issue_id"] = issueID ?: @""; item[@"issue_id"] = issueID ?: @"";
item[@"remarks"] = remarks ?: @""; item[@"remarks"] = remarks ?: @"";
// 🔥 Files
item[@"imagePaths"] = imagePaths ?: @[]; item[@"imagePaths"] = imagePaths ?: @[];
item[@"timestamp"] = @((NSInteger)[NSDate date].timeIntervalSince1970); item[@"timestamp"] = @((NSInteger)[NSDate date].timeIntervalSince1970);
if (mediaPath) item[@"mediaPath"] = mediaPath; if (mediaPath) item[@"mediaPath"] = mediaPath;
if (mediaType) item[@"mediaType"] = mediaType; if (mediaType) item[@"mediaType"] = mediaType;
// 🔥 UI display fields (CRITICAL)
item[@"projectName"] = displayInfo[@"projectName"] ?: @"Unknown Project";
item[@"unitName"] = displayInfo[@"unitName"] ?: @"-";
item[@"createdBy"] = displayInfo[@"createdBy"] ?: @"-";
item[@"issueDisplay"] = displayInfo[@"issueDisplay"] ?: @"Add Info";
item[@"createdAt"] = displayInfo[@"createdAt"] ?: [self todayDateString];
[queue addObject:item]; [queue addObject:item];
[self saveQueue:queue]; [self saveQueue:queue];
...@@ -110,18 +118,25 @@ static NSString * const kOfflineQueueKey = @"OFFLINE_SYNC_QUEUE"; ...@@ -110,18 +118,25 @@ static NSString * const kOfflineQueueKey = @"OFFLINE_SYNC_QUEUE";
- (void)enqueueAction:(NSString *)type - (void)enqueueAction:(NSString *)type
payload:(NSDictionary *)payload payload:(NSDictionary *)payload
images:(NSArray<UIImage *> *)images { images:(NSArray<UIImage *> *)images
displayInfo:(NSDictionary *)displayInfo {
NSMutableArray *queue = [self safeLoadQueue]; NSMutableArray *queue = [self safeLoadQueue];
NSArray *imagePaths = [self saveImagesToDisk:images]; NSArray *imagePaths = [self saveImagesToDisk:images];
NSDictionary *item = @{ NSMutableDictionary *item = [NSMutableDictionary dictionary];
@"id": NSUUID.UUID.UUIDString, item[@"id"] = NSUUID.UUID.UUIDString;
@"type": type, item[@"type"] = type;
@"payload": payload ?: @{}, item[@"payload"] = payload ?: @{};
@"imagePaths": imagePaths ?: @[], item[@"imagePaths"] = imagePaths ?: @[];
@"timestamp": @((NSInteger)[NSDate date].timeIntervalSince1970) item[@"timestamp"] = @((NSInteger)[NSDate date].timeIntervalSince1970);
};
// 🔥 UI fields (OG-style)
item[@"projectName"] = displayInfo[@"projectName"] ?: @"Unknown Project";
item[@"unitName"] = displayInfo[@"unitName"] ?: @"-";
item[@"createdBy"] = displayInfo[@"createdBy"] ?: @"-";
item[@"issueDisplay"] = displayInfo[@"issueDisplay"] ?: type;
item[@"createdAt"] = displayInfo[@"createdAt"] ?: [self todayDateString];
[queue addObject:item]; [queue addObject:item];
[self saveQueue:queue]; [self saveQueue:queue];
...@@ -129,30 +144,63 @@ static NSString * const kOfflineQueueKey = @"OFFLINE_SYNC_QUEUE"; ...@@ -129,30 +144,63 @@ static NSString * const kOfflineQueueKey = @"OFFLINE_SYNC_QUEUE";
NSLog(@"📥 [OfflineSync] Queued %@ (%lu)", type, (unsigned long)queue.count); NSLog(@"📥 [OfflineSync] Queued %@ (%lu)", type, (unsigned long)queue.count);
} }
#pragma mark - Processing Queue #pragma mark - Processing Queue
- (void)processQueueIfOnline { - (void)startManualSync {
if (![NetworkManager.sharedManager isConnected]) { if (![NetworkManager.sharedManager isConnected]) {
NSLog(@"🚫 [OfflineSync] No internet — aborting manual sync");
return; return;
} }
[self processNextItem];
}
- (void)processNextItem {
NSMutableArray *queue = [self safeLoadQueue]; NSMutableArray *queue = [self safeLoadQueue];
if (queue.count == 0) return; if (queue.count == 0) {
NSLog(@"✅ [OfflineSync] Queue empty — sync complete");
return;
}
NSDictionary *item = queue.firstObject; NSDictionary *item = queue.firstObject;
if (![item isKindOfClass:[NSDictionary class]]) { if (![item isKindOfClass:[NSDictionary class]]) {
NSLog(@"⚠️ [OfflineSync] Invalid item — removing");
[queue removeObjectAtIndex:0]; [queue removeObjectAtIndex:0];
[self saveQueue:queue]; [self saveQueue:queue];
[self processQueueIfOnline]; [self processNextItem];
return; return;
} }
NSLog(@"🔄 [OfflineSync] Processing %@", item[@"type"]);
[self processItem:item completion:^(BOOL success) { [self processItem:item completion:^(BOOL success) {
if (success) { if (success) {
[queue removeObjectAtIndex:0]; NSLog(@"✅ [OfflineSync] Item synced");
[self saveQueue:queue];
[self processQueueIfOnline]; // 🔥 cleanup files
for (NSString *path in item[@"imagePaths"]) {
[[NSFileManager defaultManager] removeItemAtPath:path error:nil];
}
if (item[@"mediaPath"]) {
[[NSFileManager defaultManager] removeItemAtPath:item[@"mediaPath"] error:nil];
}
// remove item and continue
NSMutableArray *updatedQueue = [self safeLoadQueue];
if (updatedQueue.count > 0) {
[updatedQueue removeObjectAtIndex:0];
[self saveQueue:updatedQueue];
}
[self processNextItem]; // move to next
} else {
NSLog(@"❌ [OfflineSync] Sync failed — stopping");
// ❗ DO NOT remove item
// user must tap Sync again
} }
}]; }];
} }
...@@ -258,9 +306,61 @@ static NSString * const kOfflineQueueKey = @"OFFLINE_SYNC_QUEUE"; ...@@ -258,9 +306,61 @@ static NSString * const kOfflineQueueKey = @"OFFLINE_SYNC_QUEUE";
} }
#pragma mark - Status #pragma mark - Status
- (BOOL)hasPendingItems { - (BOOL)hasPendingItems {
return ([self safeLoadQueue].count > 0); return ([self safeLoadQueue].count > 0);
} }
#pragma mark - Pending Screen Helper
- (NSArray *)pendingItemsForUI {
NSArray *queue = [self safeLoadQueue];
NSMutableDictionary *grouped = [NSMutableDictionary dictionary];
for (NSDictionary *item in queue) {
NSString *project = item[@"projectName"] ?: @"Unknown Project";
NSMutableArray *items = grouped[project] ?: [NSMutableArray array];
NSDictionary *uiItem = @{
@"unitName": item[@"unitName"] ?: @"-",
@"createdBy": item[@"createdBy"] ?: @"-",
@"createdOn": item[@"createdAt"] ?: [self formatTimestamp:item[@"timestamp"]],
@"type": item[@"issueDisplay"] ?: item[@"type"] ?: @"-",
@"raw": item
};
[items addObject:uiItem];
grouped[project] = items;
}
NSMutableArray *sections = [NSMutableArray array];
for (NSString *project in grouped) {
[sections addObject:@{
@"project": project,
@"items": grouped[project]
}];
}
return sections;
}
- (NSString *)formatTimestamp:(NSNumber *)ts {
NSDate *date = [NSDate dateWithTimeIntervalSince1970:ts.doubleValue];
NSDateFormatter *fmt = [[NSDateFormatter alloc] init];
fmt.dateFormat = @"yyyy-MM-dd";
return [fmt stringFromDate:date];
}
- (NSString *)todayDateString {
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.locale = [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"];
formatter.dateFormat = @"yyyy-MM-dd";
return [formatter stringFromDate:[NSDate date]];
}
- (void)clearOfflineQueue {
[LocalStorage removeObjectForKey:kOfflineQueueKey];
NSLog(@"🧹 [OfflineSync] Offline queue cleared");
}
@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