Commit f38dff39 authored by Wei Han's avatar Wei Han

code update

parent 6da6b554
...@@ -8,22 +8,22 @@ ...@@ -8,22 +8,22 @@
/* 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 */; };
1AE5006B6719358FE5D08276 /* libPods-example.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0C0CBFC285E76843D753A39C /* 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 */
0C0CBFC285E76843D753A39C /* libPods-example.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-example.a"; sourceTree = BUILT_PRODUCTS_DIR; };
0DD693481B6504D78252E1A4 /* 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>"; };
13B07F961A680F5B00A75B9A /* example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 13B07F961A680F5B00A75B9A /* example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = example.app; sourceTree = BUILT_PRODUCTS_DIR; };
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>"; };
358AE48123A5C7B504741D4F /* libPods-example.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-example.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 49B853AE2A174A2113AE36B8 /* 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>"; };
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>"; };
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; };
/* End PBXFileReference section */ /* End PBXFileReference section */
...@@ -32,7 +32,7 @@ ...@@ -32,7 +32,7 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
92F3BF4D0A6D1113D359EFA3 /* libPods-example.a in Frameworks */, 1AE5006B6719358FE5D08276 /* 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 */,
358AE48123A5C7B504741D4F /* libPods-example.a */, 0C0CBFC285E76843D753A39C /* 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 = (
9240166696F5B2EC6A36F688 /* Pods-example.debug.xcconfig */, 0DD693481B6504D78252E1A4 /* Pods-example.debug.xcconfig */,
950B7AF3DD3B011B04438348 /* Pods-example.release.xcconfig */, 49B853AE2A174A2113AE36B8 /* 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 = (
F073E18940A3F2D5B6D5B315 /* [CP] Check Pods Manifest.lock */, 421B484D7AFF49D66547AE2C /* [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 */,
9E691098E1BDE21450BE0492 /* [CP] Embed Pods Frameworks */, 1FD14DE327B38E9733D3F31E /* [CP] Embed Pods Frameworks */,
9554DF7B7A44E78B79E32173 /* [CP] Copy Pods Resources */, DCD1F955A75F9EFAB389857C /* [CP] Copy Pods Resources */,
); );
buildRules = ( buildRules = (
); );
...@@ -183,24 +183,7 @@ ...@@ -183,24 +183,7 @@
shellPath = /bin/sh; shellPath = /bin/sh;
shellScript = "set -e\n\nWITH_ENVIRONMENT=\"$REACT_NATIVE_PATH/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"$REACT_NATIVE_PATH/scripts/react-native-xcode.sh\"\n\n/bin/sh -c \"$WITH_ENVIRONMENT $REACT_NATIVE_XCODE\"\n"; shellScript = "set -e\n\nWITH_ENVIRONMENT=\"$REACT_NATIVE_PATH/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"$REACT_NATIVE_PATH/scripts/react-native-xcode.sh\"\n\n/bin/sh -c \"$WITH_ENVIRONMENT $REACT_NATIVE_XCODE\"\n";
}; };
9554DF7B7A44E78B79E32173 /* [CP] Copy Pods Resources */ = { 1FD14DE327B38E9733D3F31E /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-resources-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Copy Pods Resources";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-resources-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-resources.sh\"\n";
showEnvVarsInLog = 0;
};
9E691098E1BDE21450BE0492 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
...@@ -217,7 +200,7 @@ ...@@ -217,7 +200,7 @@
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-frameworks.sh\"\n"; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-frameworks.sh\"\n";
showEnvVarsInLog = 0; showEnvVarsInLog = 0;
}; };
F073E18940A3F2D5B6D5B315 /* [CP] Check Pods Manifest.lock */ = { 421B484D7AFF49D66547AE2C /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
...@@ -239,6 +222,23 @@ ...@@ -239,6 +222,23 @@
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0; showEnvVarsInLog = 0;
}; };
DCD1F955A75F9EFAB389857C /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-resources-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Copy Pods Resources";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-resources-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-resources.sh\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */ /* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */
...@@ -255,7 +255,7 @@ ...@@ -255,7 +255,7 @@
/* Begin XCBuildConfiguration section */ /* Begin XCBuildConfiguration section */
13B07F941A680F5B00A75B9A /* Debug */ = { 13B07F941A680F5B00A75B9A /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = 9240166696F5B2EC6A36F688 /* Pods-example.debug.xcconfig */; baseConfigurationReference = 0DD693481B6504D78252E1A4 /* 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 = 950B7AF3DD3B011B04438348 /* Pods-example.release.xcconfig */; baseConfigurationReference = 49B853AE2A174A2113AE36B8 /* 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</string> <string>ios-arm64_x86_64-simulator</string>
<key>LibraryPath</key> <key>LibraryPath</key>
<string>QmsPluginFramework.framework</string> <string>QmsPluginFramework.framework</string>
<key>SupportedArchitectures</key> <key>SupportedArchitectures</key>
<array> <array>
<string>arm64</string> <string>arm64</string>
<string>x86_64</string>
</array> </array>
<key>SupportedPlatform</key> <key>SupportedPlatform</key>
<string>ios</string> <string>ios</string>
<key>SupportedPlatformVariant</key>
<string>simulator</string>
</dict> </dict>
<dict> <dict>
<key>BinaryPath</key> <key>BinaryPath</key>
<string>QmsPluginFramework.framework/QmsPluginFramework</string> <string>QmsPluginFramework.framework/QmsPluginFramework</string>
<key>LibraryIdentifier</key> <key>LibraryIdentifier</key>
<string>ios-arm64_x86_64-simulator</string> <string>ios-arm64</string>
<key>LibraryPath</key> <key>LibraryPath</key>
<string>QmsPluginFramework.framework</string> <string>QmsPluginFramework.framework</string>
<key>SupportedArchitectures</key> <key>SupportedArchitectures</key>
<array> <array>
<string>arm64</string> <string>arm64</string>
<string>x86_64</string>
</array> </array>
<key>SupportedPlatform</key> <key>SupportedPlatform</key>
<string>ios</string> <string>ios</string>
<key>SupportedPlatformVariant</key>
<string>simulator</string>
</dict> </dict>
</array> </array>
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
......
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
</data> </data>
<key>Info.plist</key> <key>Info.plist</key>
<data> <data>
AaJE9H7xzNyQkDiLPw3sM9LfFjQ= w/Rc8R+JLx3x4AhZWV+3tG1CxCM=
</data> </data>
<key>Modules/module.modulemap</key> <key>Modules/module.modulemap</key>
<data> <data>
......
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
</data> </data>
<key>Info.plist</key> <key>Info.plist</key>
<data> <data>
RNXd5p+E7NvIdc7OKurgeIO55/s= bbhIYcGh7MnGD7V1rEns5GGhP1g=
</data> </data>
<key>Modules/module.modulemap</key> <key>Modules/module.modulemap</key>
<data> <data>
......
//
// HistoryDetailsViewController.h // HistoryDetailsViewController.h
// QmsPluginFramework #import <UIKit/UIKit.h>
//
// Created by Wei Han Ng on 19/12/2025.
//
@interface HistoryDetailsViewController : UIViewController
@property (nonatomic, strong) NSDictionary *itemData;
@end
//
// HistoryDetailsViewController.mm // HistoryDetailsViewController.mm
// QmsPluginFramework #import "HistoryDetailsViewController.h"
//
// Created by Wei Han Ng on 19/12/2025.
//
@interface HistoryDetailsViewController ()
@property (nonatomic, strong) UIView *headerView;
@property (nonatomic, strong) UIButton *backButton;
@property (nonatomic, strong) UILabel *titleLabel;
@end
@implementation HistoryDetailsViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = UIColor.systemBackgroundColor;
NSString *unitName = self.itemData[@"unitName"];
NSString *createdBy = self.itemData[@"createdBy"];
NSString *createdOn = self.itemData[@"createdOn"];
NSString *uploadedOn = self.itemData[@"uploadedOn"];
NSString *actionMade = self.itemData[@"type"];
NSString *status = self.itemData[@"status"];
NSLog(@"Unit: %@", unitName);
NSLog(@"Created by: %@", createdBy);
NSLog(@"Created on: %@", createdOn);
NSLog(@"Uploaded on: %@", uploadedOn);
NSLog(@"Action: %@", actionMade);
NSLog(@"Status: %@", status);
[self setupHeader];
}
#pragma mark - Header Setup
- (void)setupHeader {
self.headerView = [[UIView alloc] init];
self.headerView.backgroundColor = [UIColor colorWithRed:0.0 green:0.5 blue:0.6 alpha:1.0];
self.headerView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:self.headerView];
self.backButton = [UIButton buttonWithType:UIButtonTypeCustom];
[self.backButton setImage:[UIImage systemImageNamed:@"chevron.backward"]
forState:UIControlStateNormal];
self.backButton.tintColor = UIColor.whiteColor;
[self.backButton addTarget:self
action:@selector(onBack)
forControlEvents:UIControlEventTouchUpInside];
self.backButton.translatesAutoresizingMaskIntoConstraints = NO;
[self.headerView addSubview:self.backButton];
self.titleLabel = [[UILabel alloc] init];
self.titleLabel.text = @"History";
self.titleLabel.textColor = UIColor.whiteColor;
self.titleLabel.font = [UIFont boldSystemFontOfSize:18];
self.titleLabel.translatesAutoresizingMaskIntoConstraints = NO;
[self.headerView addSubview:self.titleLabel];
[NSLayoutConstraint activateConstraints:@[
[self.headerView.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor],
[self.headerView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
[self.headerView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
[self.headerView.heightAnchor constraintEqualToConstant:48],
[self.backButton.leadingAnchor constraintEqualToAnchor:self.headerView.leadingAnchor constant:8],
[self.backButton.centerYAnchor constraintEqualToAnchor:self.headerView.centerYAnchor],
[self.backButton.widthAnchor constraintEqualToConstant:32],
[self.backButton.heightAnchor constraintEqualToConstant:32],
[self.titleLabel.centerXAnchor constraintEqualToAnchor:self.headerView.centerXAnchor],
[self.titleLabel.centerYAnchor constraintEqualToAnchor:self.headerView.centerYAnchor],
]];
}
#pragma mark - Detail Content
- (void)setupDetailContent {
UIScrollView *scroll = [[UIScrollView alloc] init];
scroll.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:scroll];
UIView *container = [[UIView alloc] init];
container.translatesAutoresizingMaskIntoConstraints = NO;
[scroll addSubview:container];
[NSLayoutConstraint activateConstraints:@[
[scroll.topAnchor constraintEqualToAnchor:self.headerView.bottomAnchor],
[scroll.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
[scroll.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
[scroll.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor],
[container.topAnchor constraintEqualToAnchor:scroll.topAnchor],
[container.leadingAnchor constraintEqualToAnchor:scroll.leadingAnchor],
[container.trailingAnchor constraintEqualToAnchor:scroll.trailingAnchor],
[container.bottomAnchor constraintEqualToAnchor:scroll.bottomAnchor],
[container.widthAnchor constraintEqualToAnchor:scroll.widthAnchor],
]];
// Create vertical stack for labels
UIStackView *stack = [[UIStackView alloc] init];
stack.axis = UILayoutConstraintAxisVertical;
stack.spacing = 12;
stack.translatesAutoresizingMaskIntoConstraints = NO;
[container addSubview:stack];
[NSLayoutConstraint activateConstraints:@[
[stack.topAnchor constraintEqualToAnchor:container.topAnchor constant:16],
[stack.leadingAnchor constraintEqualToAnchor:container.leadingAnchor constant:16],
[stack.trailingAnchor constraintEqualToAnchor:container.trailingAnchor constant:-16],
[stack.bottomAnchor constraintLessThanOrEqualToAnchor:container.bottomAnchor constant:-16],
]];
// Create labels for each field
[stack addArrangedSubview:[self makeDetailLabel:@"Unit" value:self.itemData[@"unitName"]]];
[stack addArrangedSubview:[self makeDetailLabel:@"Created On" value:self.itemData[@"createdAt"]]];
[stack addArrangedSubview:[self makeDetailLabel:@"Created By" value:self.itemData[@"createdBy"]]];
[stack addArrangedSubview:[self makeDetailLabel:@"Uploaded On" value:self.itemData[@"uploadedOn"] ?: @"-"]];
[stack addArrangedSubview:[self makeDetailLabel:@"Action" value:self.itemData[@"type"]]];
[stack addArrangedSubview:[self makeDetailLabel:@"Status" value:self.itemData[@"status"] ?: @"-"]];
}
#pragma mark - Helper
- (void)onBack {
[self dismissViewControllerAnimated:YES completion:nil];
}
- (UIView *)makeDetailLabel:(NSString *)title value:(NSString *)value {
UIView *view = [[UIView alloc] init];
view.translatesAutoresizingMaskIntoConstraints = NO;
UILabel *titleLabel = [[UILabel alloc] init];
titleLabel.font = [UIFont boldSystemFontOfSize:16];
titleLabel.textColor = UIColor.labelColor;
titleLabel.text = title;
titleLabel.translatesAutoresizingMaskIntoConstraints = NO;
UILabel *valueLabel = [[UILabel alloc] init];
valueLabel.font = [UIFont systemFontOfSize:16];
valueLabel.textColor = UIColor.secondaryLabelColor;
valueLabel.text = value;
valueLabel.numberOfLines = 0;
valueLabel.translatesAutoresizingMaskIntoConstraints = NO;
[view addSubview:titleLabel];
[view addSubview:valueLabel];
[NSLayoutConstraint activateConstraints:@[
[titleLabel.topAnchor constraintEqualToAnchor:view.topAnchor],
[titleLabel.leadingAnchor constraintEqualToAnchor:view.leadingAnchor],
[titleLabel.trailingAnchor constraintEqualToAnchor:view.trailingAnchor],
[valueLabel.topAnchor constraintEqualToAnchor:titleLabel.bottomAnchor constant:4],
[valueLabel.leadingAnchor constraintEqualToAnchor:view.leadingAnchor],
[valueLabel.trailingAnchor constraintEqualToAnchor:view.trailingAnchor],
[valueLabel.bottomAnchor constraintEqualToAnchor:view.bottomAnchor],
]];
return view;
}
@end
...@@ -3,6 +3,6 @@ ...@@ -3,6 +3,6 @@
@interface PendingDetailsViewController : UIViewController @interface PendingDetailsViewController : UIViewController
@property (nonatomic, strong) NSDictionary *item; @property (nonatomic, strong) NSDictionary *itemData;
@end @end
// PendingDetailsViewController.mm // PendingDetailsViewController.mm
#import "PendingDetailsViewController.h" #import "PendingDetailsViewController.h"
@interface PendingDetailsViewController ()
@property (nonatomic, strong) UIView *headerView;
@property (nonatomic, strong) UIButton *backButton;
@property (nonatomic, strong) UILabel *titleLabel;
@end
@implementation PendingDetailsViewController @implementation PendingDetailsViewController
- (void)viewDidLoad { - (void)viewDidLoad {
...@@ -12,9 +20,9 @@ ...@@ -12,9 +20,9 @@
label.numberOfLines = 0; label.numberOfLines = 0;
label.text = [NSString stringWithFormat: label.text = [NSString stringWithFormat:
@"Unit: %@\nBy: %@\nType: %@", @"Unit: %@\nBy: %@\nType: %@",
self.item[@"unitName"], self.itemData[@"unitName"],
self.item[@"createdBy"], self.itemData[@"createdBy"],
self.item[@"type"]]; self.itemData[@"type"]];
[self.view addSubview:label]; [self.view addSubview:label];
...@@ -22,6 +30,133 @@ ...@@ -22,6 +30,133 @@
[label.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor], [label.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor],
[label.centerYAnchor constraintEqualToAnchor:self.view.centerYAnchor], [label.centerYAnchor constraintEqualToAnchor:self.view.centerYAnchor],
]]; ]];
[self setupHeader];
[self setupDetailContent];
}
#pragma mark - Header Setup
- (void)setupHeader {
self.headerView = [[UIView alloc] init];
self.headerView.backgroundColor = [UIColor colorWithRed:0.0 green:0.5 blue:0.6 alpha:1.0];
self.headerView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:self.headerView];
self.backButton = [UIButton buttonWithType:UIButtonTypeCustom];
[self.backButton setImage:[UIImage systemImageNamed:@"chevron.backward"]
forState:UIControlStateNormal];
self.backButton.tintColor = UIColor.whiteColor;
[self.backButton addTarget:self
action:@selector(onBack)
forControlEvents:UIControlEventTouchUpInside];
self.backButton.translatesAutoresizingMaskIntoConstraints = NO;
[self.headerView addSubview:self.backButton];
self.titleLabel = [[UILabel alloc] init];
self.titleLabel.text = @"Pending Items";
self.titleLabel.textColor = UIColor.whiteColor;
self.titleLabel.font = [UIFont boldSystemFontOfSize:18];
self.titleLabel.translatesAutoresizingMaskIntoConstraints = NO;
[self.headerView addSubview:self.titleLabel];
[NSLayoutConstraint activateConstraints:@[
[self.headerView.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor],
[self.headerView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
[self.headerView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
[self.headerView.heightAnchor constraintEqualToConstant:48],
[self.backButton.leadingAnchor constraintEqualToAnchor:self.headerView.leadingAnchor constant:8],
[self.backButton.centerYAnchor constraintEqualToAnchor:self.headerView.centerYAnchor],
[self.backButton.widthAnchor constraintEqualToConstant:32],
[self.backButton.heightAnchor constraintEqualToConstant:32],
[self.titleLabel.centerXAnchor constraintEqualToAnchor:self.headerView.centerXAnchor],
[self.titleLabel.centerYAnchor constraintEqualToAnchor:self.headerView.centerYAnchor],
]];
}
#pragma mark - Detail Content
- (void)setupDetailContent {
UIScrollView *scroll = [[UIScrollView alloc] init];
scroll.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:scroll];
UIView *container = [[UIView alloc] init];
container.translatesAutoresizingMaskIntoConstraints = NO;
[scroll addSubview:container];
[NSLayoutConstraint activateConstraints:@[
[scroll.topAnchor constraintEqualToAnchor:self.headerView.bottomAnchor],
[scroll.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
[scroll.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
[scroll.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor],
[container.topAnchor constraintEqualToAnchor:scroll.topAnchor],
[container.leadingAnchor constraintEqualToAnchor:scroll.leadingAnchor],
[container.trailingAnchor constraintEqualToAnchor:scroll.trailingAnchor],
[container.bottomAnchor constraintEqualToAnchor:scroll.bottomAnchor],
[container.widthAnchor constraintEqualToAnchor:scroll.widthAnchor],
]];
// Create vertical stack for labels
UIStackView *stack = [[UIStackView alloc] init];
stack.axis = UILayoutConstraintAxisVertical;
stack.spacing = 12;
stack.translatesAutoresizingMaskIntoConstraints = NO;
[container addSubview:stack];
[NSLayoutConstraint activateConstraints:@[
[stack.topAnchor constraintEqualToAnchor:container.topAnchor constant:16],
[stack.leadingAnchor constraintEqualToAnchor:container.leadingAnchor constant:16],
[stack.trailingAnchor constraintEqualToAnchor:container.trailingAnchor constant:-16],
[stack.bottomAnchor constraintLessThanOrEqualToAnchor:container.bottomAnchor constant:-16],
]];
// Create labels for each field
[stack addArrangedSubview:[self makeDetailLabel:@"Unit" value:self.itemData[@"unitName"]]];
[stack addArrangedSubview:[self makeDetailLabel:@"Created On" value:self.itemData[@"createdAt"]]];
[stack addArrangedSubview:[self makeDetailLabel:@"Created By" value:self.itemData[@"createdBy"]]];
[stack addArrangedSubview:[self makeDetailLabel:@"Action" value:self.itemData[@"type"]]];
[stack addArrangedSubview:[self makeDetailLabel:@"Status" value:self.itemData[@"status"] ?: @"-"]];
}
#pragma mark - Helper
- (void)onBack {
[self dismissViewControllerAnimated:YES completion:nil];
}
- (UIView *)makeDetailLabel:(NSString *)title value:(NSString *)value {
UIView *view = [[UIView alloc] init];
view.translatesAutoresizingMaskIntoConstraints = NO;
UILabel *titleLabel = [[UILabel alloc] init];
titleLabel.font = [UIFont boldSystemFontOfSize:16];
titleLabel.textColor = UIColor.labelColor;
titleLabel.text = title;
titleLabel.translatesAutoresizingMaskIntoConstraints = NO;
UILabel *valueLabel = [[UILabel alloc] init];
valueLabel.font = [UIFont systemFontOfSize:16];
valueLabel.textColor = UIColor.secondaryLabelColor;
valueLabel.text = value;
valueLabel.numberOfLines = 0;
valueLabel.translatesAutoresizingMaskIntoConstraints = NO;
[view addSubview:titleLabel];
[view addSubview:valueLabel];
[NSLayoutConstraint activateConstraints:@[
[titleLabel.topAnchor constraintEqualToAnchor:view.topAnchor],
[titleLabel.leadingAnchor constraintEqualToAnchor:view.leadingAnchor],
[titleLabel.trailingAnchor constraintEqualToAnchor:view.trailingAnchor],
[valueLabel.topAnchor constraintEqualToAnchor:titleLabel.bottomAnchor constant:4],
[valueLabel.leadingAnchor constraintEqualToAnchor:view.leadingAnchor],
[valueLabel.trailingAnchor constraintEqualToAnchor:view.trailingAnchor],
[valueLabel.bottomAnchor constraintEqualToAnchor:view.bottomAnchor],
]];
return view;
} }
@end @end
// SyncProgressOverlay.h
@interface SyncProgressOverlay : UIView
@property (nonatomic, strong) UILabel *progressLabel;
@property (nonatomic, strong) UIProgressView *progressBar;
- (void)updateStep:(NSInteger)step total:(NSInteger)total title:(NSString *)title;
@end
// SyncProgressOverlay.m
#import <UIKit/UIKit.h>
#import "SyncProgressOverlay.h"
@implementation SyncProgressOverlay
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (!self) return nil;
self.backgroundColor = [[UIColor blackColor] colorWithAlphaComponent:0.4];
self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
UIView *card = [[UIView alloc] init];
card.backgroundColor = UIColor.whiteColor;
card.layer.cornerRadius = 12;
card.translatesAutoresizingMaskIntoConstraints = NO;
[self addSubview:card];
[NSLayoutConstraint activateConstraints:@[
[card.centerXAnchor constraintEqualToAnchor:self.centerXAnchor],
[card.centerYAnchor constraintEqualToAnchor:self.centerYAnchor],
[card.widthAnchor constraintEqualToConstant:260]
]];
self.progressLabel = [[UILabel alloc] init];
self.progressLabel.textAlignment = NSTextAlignmentCenter;
self.progressLabel.font = [UIFont systemFontOfSize:14];
self.progressLabel.translatesAutoresizingMaskIntoConstraints = NO;
self.progressBar = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault];
self.progressBar.progress = 0;
self.progressBar.translatesAutoresizingMaskIntoConstraints = NO;
UIStackView *stack = [[UIStackView alloc] initWithArrangedSubviews:@[self.progressLabel, self.progressBar]];
stack.axis = UILayoutConstraintAxisVertical;
stack.spacing = 12;
stack.translatesAutoresizingMaskIntoConstraints = NO;
[card addSubview:stack];
[NSLayoutConstraint activateConstraints:@[
[stack.topAnchor constraintEqualToAnchor:card.topAnchor constant:20],
[stack.leadingAnchor constraintEqualToAnchor:card.leadingAnchor constant:16],
[stack.trailingAnchor constraintEqualToAnchor:card.trailingAnchor constant:-16],
[stack.bottomAnchor constraintEqualToAnchor:card.bottomAnchor constant:-20],
]];
return self;
}
- (void)updateStep:(NSInteger)step total:(NSInteger)total title:(NSString *)title {
self.progressLabel.text = title ?: [NSString stringWithFormat:@"%ld / %ld", step, total];
self.progressBar.progress = total > 0 ? (float)step / (float)total : 0;
}
@end
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
#import "SyncHistoryViewController.h" #import "SyncHistoryViewController.h"
#import "OfflineSyncManager.h" #import "OfflineSyncManager.h"
#import "SyncProgressOverlay.h"
#import "HistoryDetailsViewController.h"
typedef NS_ENUM(NSInteger, HistorySyncStatus) { typedef NS_ENUM(NSInteger, HistorySyncStatus) {
HistorySyncStatusSuccess, HistorySyncStatusSuccess,
...@@ -45,7 +47,7 @@ typedef NS_ENUM(NSInteger, HistorySyncStatus) { ...@@ -45,7 +47,7 @@ typedef NS_ENUM(NSInteger, HistorySyncStatus) {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (!self) return nil; if (!self) return nil;
self.selectionStyle = UITableViewCellSelectionStyleNone; self.selectionStyle = UITableViewCellSelectionStyleDefault;
self.backgroundColor = UIColor.clearColor; // 👈 important self.backgroundColor = UIColor.clearColor; // 👈 important
self.contentView.backgroundColor = UIColor.whiteColor; self.contentView.backgroundColor = UIColor.whiteColor;
...@@ -141,6 +143,8 @@ typedef NS_ENUM(NSInteger, HistorySyncStatus) { ...@@ -141,6 +143,8 @@ 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;
@property (nonatomic, strong) SyncProgressOverlay *progressOverlay;
@end @end
@implementation SyncHistoryViewController @implementation SyncHistoryViewController
...@@ -152,11 +156,11 @@ typedef NS_ENUM(NSInteger, HistorySyncStatus) { ...@@ -152,11 +156,11 @@ typedef NS_ENUM(NSInteger, HistorySyncStatus) {
self.selectedTab = 0; self.selectedTab = 0;
[self setupHeader]; [self setupHeader];
[self setupOfflineBanner]; // [self setupOfflineBanner];
[self setupTabBar]; [self setupTabBar];
[self setupBottomButton];
[self setupTableView]; [self setupTableView];
[self setupEmptyState]; [self setupEmptyState];
[self setupBottomButton];
[self loadHistoryData]; [self loadHistoryData];
[self applyFilter]; [self applyFilter];
...@@ -226,32 +230,31 @@ typedef NS_ENUM(NSInteger, HistorySyncStatus) { ...@@ -226,32 +230,31 @@ typedef NS_ENUM(NSInteger, HistorySyncStatus) {
]]; ]];
} }
#pragma mark - Setup Offline Banner #pragma mark - Setup Offline Banner
- (void)setupOfflineBanner { //- (void)setupOfflineBanner {
self.offlineBanner = [[UIView alloc] init]; // self.offlineBanner = [[UIView alloc] init];
self.offlineBanner.backgroundColor = [UIColor colorWithRed:1.0 green:0.9 blue:0.6 alpha:1.0]; // self.offlineBanner.backgroundColor = [UIColor colorWithRed:1.0 green:0.9 blue:0.6 alpha:1.0];
self.offlineBanner.translatesAutoresizingMaskIntoConstraints = NO; // self.offlineBanner.translatesAutoresizingMaskIntoConstraints = NO;
self.offlineBanner.hidden = YES; // self.offlineBanner.hidden = YES;
//
UILabel *label = [[UILabel alloc] init]; // UILabel *label = [[UILabel alloc] init];
label.text = @"Offline Mode"; // label.text = @"Offline Mode";
label.font = [UIFont boldSystemFontOfSize:14]; // label.font = [UIFont boldSystemFontOfSize:14];
label.translatesAutoresizingMaskIntoConstraints = NO; // label.translatesAutoresizingMaskIntoConstraints = NO;
//
[self.offlineBanner addSubview:label]; // [self.offlineBanner addSubview:label];
[self.view addSubview:self.offlineBanner]; // [self.view addSubview:self.offlineBanner];
//
[NSLayoutConstraint activateConstraints:@[ // [NSLayoutConstraint activateConstraints:@[
[self.offlineBanner.topAnchor constraintEqualToAnchor:self.headerView.bottomAnchor], // [self.offlineBanner.topAnchor constraintEqualToAnchor:self.headerView.bottomAnchor],
[self.offlineBanner.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor], // [self.offlineBanner.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
[self.offlineBanner.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor], // [self.offlineBanner.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
[self.offlineBanner.heightAnchor constraintEqualToConstant:36], // [self.offlineBanner.heightAnchor constraintEqualToConstant:36],
//
[label.centerXAnchor constraintEqualToAnchor:self.offlineBanner.centerXAnchor], // [label.centerXAnchor constraintEqualToAnchor:self.offlineBanner.centerXAnchor],
[label.centerYAnchor constraintEqualToAnchor:self.offlineBanner.centerYAnchor], // [label.centerYAnchor constraintEqualToAnchor:self.offlineBanner.centerYAnchor],
]]; // ]];
} //}
#pragma mark - Setup Tabbar #pragma mark - Setup Tabbar
- (void)setupTabBar { - (void)setupTabBar {
...@@ -315,25 +318,33 @@ typedef NS_ENUM(NSInteger, HistorySyncStatus) { ...@@ -315,25 +318,33 @@ typedef NS_ENUM(NSInteger, HistorySyncStatus) {
[self.tableView.topAnchor constraintEqualToAnchor:self.tabBarView.bottomAnchor constant:30], [self.tableView.topAnchor constraintEqualToAnchor:self.tabBarView.bottomAnchor constant:30],
[self.tableView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor], [self.tableView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
[self.tableView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor], [self.tableView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
[self.tableView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor constant:-60], [self.tableView.bottomAnchor constraintEqualToAnchor:self.syncAgainButton.topAnchor constant:15],
]]; ]];
} }
#pragma mark - Data #pragma mark - Progress Overlay
- (void)loadHistoryData { - (void)showProgressOverlay {
if (self.progressOverlay) return;
NSArray *sections = self.progressOverlay =
[[OfflineSyncManager shared] historyItemsForUI]; [[SyncProgressOverlay alloc] initWithFrame:self.view.bounds];
[self.view addSubview:self.progressOverlay];
}
#pragma mark - Data
- (void)loadHistoryData {
NSArray *sections = [[OfflineSyncManager shared] historyItemsForUI];
[self dumpHistoryToFile:sections]; [self dumpHistoryToFile:sections];
NSMutableArray *items = [NSMutableArray array]; NSMutableArray *items = [NSMutableArray array];
for (NSDictionary *section in sections) { for (NSDictionary *section in sections) {
// Check if the section has "items" key or is itself an item
NSArray *rows = section[@"items"]; NSArray *rows = section[@"items"];
if (!rows) rows = @[section]; // <-- fallback
for (NSDictionary *row in rows) { for (NSDictionary *row in rows) {
HistorySyncItem *item = [HistorySyncItem new]; HistorySyncItem *item = [HistorySyncItem new];
item.unitName = row[@"unitName"]; item.unitName = row[@"unitName"];
item.createdBy = row[@"createdBy"]; item.createdBy = row[@"createdBy"];
...@@ -342,18 +353,17 @@ typedef NS_ENUM(NSInteger, HistorySyncStatus) { ...@@ -342,18 +353,17 @@ typedef NS_ENUM(NSInteger, HistorySyncStatus) {
item.actionMade = row[@"type"]; item.actionMade = row[@"type"];
NSString *status = row[@"status"]; NSString *status = row[@"status"];
item.status = item.status = [status isEqualToString:@"FAILED"]
[status isEqualToString:@"FAILED"]
? HistorySyncStatusFailed ? HistorySyncStatusFailed
: HistorySyncStatusSuccess; : HistorySyncStatusSuccess;
item.raw = row; item.raw = row;
[items addObject:item]; [items addObject:item];
} }
} }
self.allItems = items; self.allItems = items;
[self updateSyncAgainButtonState];
} }
// Dummy data // Dummy data
...@@ -382,12 +392,14 @@ typedef NS_ENUM(NSInteger, HistorySyncStatus) { ...@@ -382,12 +392,14 @@ typedef NS_ENUM(NSInteger, HistorySyncStatus) {
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:YES]; [tableView deselectRowAtIndexPath:indexPath animated:YES];
NSLog(@"Tapped section %ld row %ld", (long)indexPath.section, (long)indexPath.row);
UIViewController *detail = [UIViewController new]; HistorySyncItem *selectedItem = self.filteredItems[indexPath.row];
detail.view.backgroundColor = UIColor.whiteColor;
detail.title = @"History Detail";
[self.navigationController pushViewController:detail animated:YES]; HistoryDetailsViewController *vc = [HistoryDetailsViewController new];
vc.itemData = selectedItem.raw;
vc.modalPresentationStyle = UIModalPresentationFullScreen;
[self presentViewController:vc animated:YES completion:nil];
} }
- (void)setupEmptyState { - (void)setupEmptyState {
...@@ -518,19 +530,50 @@ typedef NS_ENUM(NSInteger, HistorySyncStatus) { ...@@ -518,19 +530,50 @@ typedef NS_ENUM(NSInteger, HistorySyncStatus) {
self.emptyStateView.hidden = (self.filteredItems.count > 0); self.emptyStateView.hidden = (self.filteredItems.count > 0);
[self.tableView reloadData]; [self.tableView reloadData];
[self updateSyncAgainButtonState];
} }
- (void)onSyncAgain { - (void)onSyncAgain {
BOOL hasRetryItems = NO;
for (HistorySyncItem *item in self.allItems) { for (HistorySyncItem *item in self.allItems) {
if (item.status == HistorySyncStatusFailed) { if (item.status == HistorySyncStatusFailed) {
[[OfflineSyncManager shared] retryHistoryItem:item.raw]; [[OfflineSyncManager shared] retryHistoryItem:item.raw];
hasRetryItems = YES;
} }
} }
NSLog(@"Starting sync again"); if (!hasRetryItems) {
// [[OfflineSyncManager shared] startManualSync]; NSLog(@"No failed items to retry");
return;
}
NSLog(@"🔁 Starting sync again");
[self showProgressOverlay];
[[OfflineSyncManager shared]
processNextItemWithProgress:^(NSInteger step,
NSInteger total,
NSString *title) {
NSLog(@"🔄 Retry %ld / %ld – %@", (long)step, (long)total, title);
[self.progressOverlay updateStep:step
total:total
title:@"Retrying History Items"];
} completion:^(BOOL finished) {
NSLog(@"✅ Retry sync completed");
dispatch_async(dispatch_get_main_queue(), ^{
[self.progressOverlay removeFromSuperview];
self.progressOverlay = nil;
[self loadHistoryData]; [self loadHistoryData];
[self applyFilter]; [self applyFilter];
});
}];
} }
- (void)dumpHistoryToFile:(id)history { - (void)dumpHistoryToFile:(id)history {
...@@ -560,5 +603,19 @@ typedef NS_ENUM(NSInteger, HistorySyncStatus) { ...@@ -560,5 +603,19 @@ typedef NS_ENUM(NSInteger, HistorySyncStatus) {
} }
} }
- (void)updateSyncAgainButtonState {
BOOL hasFailedItems = NO;
for (HistorySyncItem *item in self.allItems) {
if (item.status == HistorySyncStatusFailed) {
hasFailedItems = YES;
break;
}
}
self.syncAgainButton.enabled = hasFailedItems;
self.syncAgainButton.backgroundColor =
hasFailedItems ? [UIColor systemBlueColor] : [UIColor lightGrayColor];
}
@end @end
...@@ -31,7 +31,7 @@ typedef NS_ENUM(NSInteger, SyncPendingViewMode) { ...@@ -31,7 +31,7 @@ typedef NS_ENUM(NSInteger, SyncPendingViewMode) {
if (self) { if (self) {
self.backgroundColor = UIColor.clearColor; self.backgroundColor = UIColor.clearColor;
self.selectionStyle = UITableViewCellSelectionStyleNone; self.selectionStyle = UITableViewCellSelectionStyleGray;
self.cardView = [[UIView alloc] init]; self.cardView = [[UIView alloc] init];
self.cardView.backgroundColor = UIColor.whiteColor; self.cardView.backgroundColor = UIColor.whiteColor;
...@@ -41,6 +41,7 @@ typedef NS_ENUM(NSInteger, SyncPendingViewMode) { ...@@ -41,6 +41,7 @@ typedef NS_ENUM(NSInteger, SyncPendingViewMode) {
self.cardView.layer.shadowRadius = 4; self.cardView.layer.shadowRadius = 4;
self.cardView.layer.shadowOffset = CGSizeMake(0, 2); self.cardView.layer.shadowOffset = CGSizeMake(0, 2);
self.cardView.translatesAutoresizingMaskIntoConstraints = NO; self.cardView.translatesAutoresizingMaskIntoConstraints = NO;
self.cardView.userInteractionEnabled = YES;
[self.contentView addSubview:self.cardView]; [self.contentView addSubview:self.cardView];
...@@ -238,6 +239,7 @@ typedef NS_ENUM(NSInteger, SyncPendingViewMode) { ...@@ -238,6 +239,7 @@ typedef NS_ENUM(NSInteger, SyncPendingViewMode) {
self.tableView.dataSource = self; self.tableView.dataSource = self;
self.tableView.backgroundColor = UIColor.clearColor; self.tableView.backgroundColor = UIColor.clearColor;
self.tableView.estimatedRowHeight = 120; self.tableView.estimatedRowHeight = 120;
self.tableView.allowsSelection = YES;
[self.tableView registerClass:SyncPendingCardCell.class [self.tableView registerClass:SyncPendingCardCell.class
forCellReuseIdentifier:@"CardCell"]; forCellReuseIdentifier:@"CardCell"];
...@@ -326,8 +328,8 @@ typedef NS_ENUM(NSInteger, SyncPendingViewMode) { ...@@ -326,8 +328,8 @@ typedef NS_ENUM(NSInteger, SyncPendingViewMode) {
return cell; return cell;
} }
- (void)tableView:(UITableView *)tableView - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
didSelectRowAtIndexPath:(NSIndexPath *)indexPath { NSLog(@"Tapped section %ld row %ld", (long)indexPath.section, (long)indexPath.row);
NSDictionary *item = NSDictionary *item =
self.sections[indexPath.section][@"items"][indexPath.row]; self.sections[indexPath.section][@"items"][indexPath.row];
...@@ -365,8 +367,9 @@ heightForHeaderInSection:(NSInteger)section { ...@@ -365,8 +367,9 @@ heightForHeaderInSection:(NSInteger)section {
- (void)showDetailsForItem:(NSDictionary *)item { - (void)showDetailsForItem:(NSDictionary *)item {
PendingDetailsViewController *vc = [PendingDetailsViewController new]; PendingDetailsViewController *vc = [PendingDetailsViewController new];
vc.item = item; vc.itemData = item;
[self.navigationController pushViewController:vc animated:YES]; vc.modalPresentationStyle = UIModalPresentationFullScreen;
[self presentViewController:vc animated:YES completion:nil];
} }
- (void)onShare { - (void)onShare {
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#import "SyncProjectViewController.h" #import "SyncProjectViewController.h"
#import "LocalStorage.h" #import "LocalStorage.h"
#import "OfflineSyncManager.h" #import "OfflineSyncManager.h"
#import "SyncProgressOverlay.h"
typedef NS_ENUM(NSInteger, SyncCardType) { typedef NS_ENUM(NSInteger, SyncCardType) {
SyncCardTypeOfflineUnits = 1, SyncCardTypeOfflineUnits = 1,
...@@ -50,9 +51,7 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE"; ...@@ -50,9 +51,7 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE";
@property (nonatomic, strong) UIButton *submitPendingToggle; @property (nonatomic, strong) UIButton *submitPendingToggle;
@property (nonatomic, strong) UIButton *downloadUnitsToggle; @property (nonatomic, strong) UIButton *downloadUnitsToggle;
@property (nonatomic, strong) UIView *syncProgressOverlay; @property (nonatomic, strong) SyncProgressOverlay *progressOverlay;
@property (nonatomic, strong) UIProgressView *progressBar;
@property (nonatomic, strong) UILabel *progressLabel;
@end @end
...@@ -432,54 +431,14 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE"; ...@@ -432,54 +431,14 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE";
#pragma mark - Progress Overlay #pragma mark - Progress Overlay
- (void)showProgressOverlay { - (void)showProgressOverlay {
self.syncProgressOverlay = [[UIView alloc] initWithFrame:self.view.bounds]; if (self.progressOverlay) return;
self.syncProgressOverlay.backgroundColor =
[[UIColor blackColor] colorWithAlphaComponent:0.4];
self.syncProgressOverlay.autoresizingMask =
UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[self.view addSubview:self.syncProgressOverlay];
UIView *card = [[UIView alloc] init]; self.progressOverlay =
card.backgroundColor = UIColor.whiteColor; [[SyncProgressOverlay alloc] initWithFrame:self.view.bounds];
card.layer.cornerRadius = 12;
card.translatesAutoresizingMaskIntoConstraints = NO;
[self.syncProgressOverlay addSubview:card];
[NSLayoutConstraint activateConstraints:@[ [self.view addSubview:self.progressOverlay];
[card.centerXAnchor constraintEqualToAnchor:self.syncProgressOverlay.centerXAnchor],
[card.centerYAnchor constraintEqualToAnchor:self.syncProgressOverlay.centerYAnchor],
[card.widthAnchor constraintEqualToConstant:260]
]];
self.progressLabel = [[UILabel alloc] init];
self.progressLabel.textAlignment = NSTextAlignmentCenter;
self.progressLabel.font = [UIFont systemFontOfSize:14];
self.progressLabel.translatesAutoresizingMaskIntoConstraints = NO;
self.progressBar = [[UIProgressView alloc] initWithProgressViewStyle:UIProgressViewStyleDefault];
self.progressBar.progress = 0;
self.progressBar.translatesAutoresizingMaskIntoConstraints = NO;
UIStackView *stack = [[UIStackView alloc] initWithArrangedSubviews:@[
self.progressLabel,
self.progressBar
]];
stack.axis = UILayoutConstraintAxisVertical;
stack.spacing = 12;
stack.translatesAutoresizingMaskIntoConstraints = NO;
[card addSubview:stack];
[NSLayoutConstraint activateConstraints:@[
[stack.topAnchor constraintEqualToAnchor:card.topAnchor constant:20],
[stack.leadingAnchor constraintEqualToAnchor:card.leadingAnchor constant:16],
[stack.trailingAnchor constraintEqualToAnchor:card.trailingAnchor constant:-16],
[stack.bottomAnchor constraintEqualToAnchor:card.bottomAnchor constant:-20],
]];
} }
#pragma mark - Helpers #pragma mark - Helpers
- (void)onBack { - (void)onBack {
[self dismissViewControllerAnimated:YES completion:nil]; [self dismissViewControllerAnimated:YES completion:nil];
...@@ -572,11 +531,9 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE"; ...@@ -572,11 +531,9 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE";
[[OfflineSyncManager shared] [[OfflineSyncManager shared]
processNextItemWithProgress:^(NSInteger step, NSInteger total, NSString *title) { processNextItemWithProgress:^(NSInteger step, NSInteger total, NSString *title) {
self.progressLabel.text = [self.progressOverlay updateStep:step
[NSString stringWithFormat:@"%ld / %ld", step, total]; total:total
self.titleLabel.text = title; title:title];
self.progressBar.progress =
total > 0 ? (float)step / (float)total : 0;
} completion:^(BOOL finished) { } completion:^(BOOL finished) {
...@@ -593,12 +550,9 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE"; ...@@ -593,12 +550,9 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE";
[[OfflineSyncManager shared] [[OfflineSyncManager shared]
startDownloadUnitImagesWithProgress:^(NSInteger completed, NSInteger total) { startDownloadUnitImagesWithProgress:^(NSInteger completed, NSInteger total) {
weakSelf.progressLabel.text = [self.progressOverlay updateStep:completed
[NSString stringWithFormat:@"Downloading Units (%ld/%ld)", total:total
(long)completed, (long)total]; title:@"Downloading Units"];
weakSelf.progressBar.progress =
total > 0 ? (float)completed / (float)total : 0;
} completion:^(BOOL success) { } completion:^(BOOL success) {
...@@ -638,8 +592,8 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE"; ...@@ -638,8 +592,8 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE";
} }
- (void)finishSyncWithSuccess:(BOOL)success { - (void)finishSyncWithSuccess:(BOOL)success {
[self.syncProgressOverlay removeFromSuperview]; [self.progressOverlay removeFromSuperview];
self.syncProgressOverlay = nil; self.progressOverlay = nil;
self.isSyncing = NO; self.isSyncing = NO;
......
...@@ -40,6 +40,8 @@ NS_ASSUME_NONNULL_BEGIN ...@@ -40,6 +40,8 @@ NS_ASSUME_NONNULL_BEGIN
// Retry failed history item // Retry failed history item
- (void)retryHistoryItem:(NSDictionary *)historyItem; - (void)retryHistoryItem:(NSDictionary *)historyItem;
- (void)retryAllFailedHistoryItems;
// Download Units // Download Units
- (void)startDownloadUnitImagesWithProgress:(void (^)(NSInteger completed, NSInteger total))progress - (void)startDownloadUnitImagesWithProgress:(void (^)(NSInteger completed, NSInteger total))progress
completion:(void (^)(BOOL success))completion; completion:(void (^)(BOOL success))completion;
......
...@@ -160,12 +160,16 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE"; ...@@ -160,12 +160,16 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE";
} }
__block NSInteger step = 0; __block NSInteger step = 0;
__weak typeof(self) weakSelf = self; __weak typeof(self) weakSelf = self;
void (^processNext)(void); __block void (^processNext)(void);
__weak __block void (^weakProcessNext)(void);
processNext = ^{ processNext = ^{
NSMutableArray *currentQueue = [weakSelf safeLoadQueue]; __strong typeof(weakSelf) strongSelf = weakSelf;
if (!strongSelf) return;
NSMutableArray *currentQueue = [strongSelf safeLoadQueue];
if (currentQueue.count == 0) { if (currentQueue.count == 0) {
if (completion) completion(YES); if (completion) completion(YES);
...@@ -180,18 +184,18 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE"; ...@@ -180,18 +184,18 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE";
progress(step, total, title); progress(step, total, title);
} }
[weakSelf processItem:item completion:^(BOOL success) { [strongSelf processItem:item completion:^(BOOL success) {
// 🔥 Save to history // Save to history
NSMutableDictionary *historyItem = [item mutableCopy]; NSMutableDictionary *historyItem = [item mutableCopy];
historyItem[@"syncedAt"] = [weakSelf todayDateString]; historyItem[@"syncedAt"] = [strongSelf todayDateString];
historyItem[@"status"] = success ? @"SUCCESS" : @"FAILED"; historyItem[@"status"] = success ? @"SUCCESS" : @"FAILED";
NSMutableArray *history = [weakSelf safeLoadHistory]; NSMutableArray *history = [strongSelf safeLoadHistory];
[history insertObject:historyItem atIndex:0]; [history insertObject:historyItem atIndex:0];
[weakSelf saveHistoryQueue:history]; [strongSelf 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];
} }
...@@ -199,20 +203,19 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE"; ...@@ -199,20 +203,19 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE";
[[NSFileManager defaultManager] removeItemAtPath:item[@"mediaPath"] error:nil]; [[NSFileManager defaultManager] removeItemAtPath:item[@"mediaPath"] error:nil];
} }
// 🔥 Remove processed item // Remove processed item
NSMutableArray *updatedQueue = [weakSelf safeLoadQueue]; NSMutableArray *updatedQueue = [strongSelf safeLoadQueue];
if (updatedQueue.count > 0) { if (updatedQueue.count > 0) {
[updatedQueue removeObjectAtIndex:0]; [updatedQueue removeObjectAtIndex:0];
[weakSelf saveQueue:updatedQueue]; [strongSelf saveQueue:updatedQueue];
} }
// 🔁 Continue
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
processNext(); if (weakProcessNext) weakProcessNext();
}); });
}]; }];
}; };
weakProcessNext = processNext;
processNext(); processNext();
} }
...@@ -229,12 +232,15 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE"; ...@@ -229,12 +232,15 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE";
NSTimeInterval timeout = 60.0; // seconds NSTimeInterval timeout = 60.0; // seconds
__block BOOL completed = NO; __block BOOL completed = NO;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(timeout * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ __weak typeof(self) weakSelf = self;
if (!completed) {
NSLog(@"⚠️ [OfflineSync] Item timed out: %@, marking as FAILED", item[@"type"]); dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(timeout * NSEC_PER_SEC)),
dispatch_get_main_queue(), ^{
if (!weakSelf || completed) return;
NSLog(@"⚠️ [OfflineSync] Item timed out: %@", item[@"type"]);
completed = YES; completed = YES;
completion(NO); completion(NO);
}
}); });
void (^finalize)(BOOL) = ^(BOOL success){ void (^finalize)(BOOL) = ^(BOOL success){
...@@ -251,7 +257,8 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE"; ...@@ -251,7 +257,8 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE";
[APIClient submitAddInfo:item[@"issue_id"] [APIClient submitAddInfo:item[@"issue_id"]
remarks:item[@"remarks"] remarks:item[@"remarks"]
completion:^(NSDictionary *res, NSError *err) { completion:^(NSDictionary *res, NSError *err) {
finalize(err == nil); BOOL success = [self isAPISuccess:res error:err];
finalize(success);
}]; }];
} else { } else {
// ADD_INFO with images/media // ADD_INFO with images/media
...@@ -261,7 +268,8 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE"; ...@@ -261,7 +268,8 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE";
mediaURL:mediaURL mediaURL:mediaURL
mediaType:item[@"mediaType"] mediaType:item[@"mediaType"]
completion:^(NSDictionary *res, NSError *err) { completion:^(NSDictionary *res, NSError *err) {
finalize(err == nil); BOOL success = [self isAPISuccess:res error:err];
finalize(success);
}]; }];
} }
return; return;
...@@ -271,16 +279,25 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE"; ...@@ -271,16 +279,25 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE";
[APIClient submitAddIssue:item[@"payload"] [APIClient submitAddIssue:item[@"payload"]
images:images images:images
completion:^(NSDictionary *res, NSError *err){ completion:^(NSDictionary *res, NSError *err){
finalize(err == nil);
[self logAddIssueDebugWithItem:item
images:images
res:res
err:err];
BOOL success = [self isAPISuccess:res error:err];
finalize(success);
}]; }];
return; return;
} }
else if ([type isEqualToString:@"UPDATE_ISSUE"]) { else if ([type isEqualToString:@"UPDATE_ISSUE"]) {
[APIClient submitUpdateIssue:item[@"payload"] [APIClient submitUpdateIssue:item[@"payload"]
images:images images:images
completion:^(NSDictionary *res, NSError *err){ completion:^(NSDictionary *res, NSError *err){
finalize(err == nil); BOOL success = [self isAPISuccess:res error:err];
finalize(success);
}]; }];
return; return;
} }
...@@ -466,8 +483,12 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE"; ...@@ -466,8 +483,12 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE";
- (void)retryHistoryItem:(NSDictionary *)historyItem { - (void)retryHistoryItem:(NSDictionary *)historyItem {
NSMutableDictionary *queueItem = [historyItem mutableCopy];
[queueItem removeObjectForKey:@"status"];
[queueItem removeObjectForKey:@"syncedAt"];
NSMutableArray *queue = [self safeLoadQueue]; NSMutableArray *queue = [self safeLoadQueue];
[queue addObject:historyItem]; [queue addObject:queueItem];
[self saveQueue:queue]; [self saveQueue:queue];
NSMutableArray *history = [self loadHistoryQueue]; NSMutableArray *history = [self loadHistoryQueue];
...@@ -475,7 +496,6 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE"; ...@@ -475,7 +496,6 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE";
[self saveHistoryQueue:history]; [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];
...@@ -773,5 +793,55 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE"; ...@@ -773,5 +793,55 @@ static NSString * const kLastOfflineSyncDateKey = @"LAST_OFFLINE_SYNC_DATE";
forKey:kLastOfflineSyncDateKey]; forKey:kLastOfflineSyncDateKey];
} }
#pragma mark - Pending Items Helpers
- (BOOL)isAPISuccess:(NSDictionary *)res error:(NSError *)err {
if (err || ![res isKindOfClass:[NSDictionary class]]) {
return NO;
}
NSDictionary *appData = res[@"AppData"];
if (![appData isKindOfClass:[NSDictionary class]]) {
return NO;
}
NSString *status = appData[@"status"];
return [status isKindOfClass:[NSString class]] &&
[[status lowercaseString] isEqualToString:@"success"];
}
- (void)logAddIssueDebugWithItem:(NSDictionary *)item
images:(NSArray<UIImage *> *)images
res:(NSDictionary *)res
err:(NSError *)err {
NSLog(@"\n🚀 [ADD_ISSUE] ================================");
NSLog(@"📤 Payload: %@", item[@"payload"]);
NSLog(@"📸 Images count: %lu", (unsigned long)images.count);
NSMutableArray *imageSizes = [NSMutableArray array];
for (UIImage *img in images) {
CGSize size = img.size;
[imageSizes addObject:
[NSString stringWithFormat:@"%.0fx%.0f", size.width, size.height]];
}
NSLog(@"📸 Image sizes: %@", imageSizes);
if (err) {
NSLog(@"❌ API Error: %@", err);
} else {
NSLog(@"📥 API Response: %@", res);
}
NSDictionary *appData = res[@"AppData"];
NSLog(@"📥 AppData: %@", appData);
NSLog(@"🏁 Status evaluated as: %@",
[self isAPISuccess:res error:err] ? @"SUCCESS" : @"FAILED");
NSLog(@"============================================\n");
}
@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</string> <string>ios-arm64_x86_64-simulator</string>
<key>LibraryPath</key> <key>LibraryPath</key>
<string>QmsPluginFramework.framework</string> <string>QmsPluginFramework.framework</string>
<key>SupportedArchitectures</key> <key>SupportedArchitectures</key>
<array> <array>
<string>arm64</string> <string>arm64</string>
<string>x86_64</string>
</array> </array>
<key>SupportedPlatform</key> <key>SupportedPlatform</key>
<string>ios</string> <string>ios</string>
<key>SupportedPlatformVariant</key>
<string>simulator</string>
</dict> </dict>
<dict> <dict>
<key>BinaryPath</key> <key>BinaryPath</key>
<string>QmsPluginFramework.framework/QmsPluginFramework</string> <string>QmsPluginFramework.framework/QmsPluginFramework</string>
<key>LibraryIdentifier</key> <key>LibraryIdentifier</key>
<string>ios-arm64_x86_64-simulator</string> <string>ios-arm64</string>
<key>LibraryPath</key> <key>LibraryPath</key>
<string>QmsPluginFramework.framework</string> <string>QmsPluginFramework.framework</string>
<key>SupportedArchitectures</key> <key>SupportedArchitectures</key>
<array> <array>
<string>arm64</string> <string>arm64</string>
<string>x86_64</string>
</array> </array>
<key>SupportedPlatform</key> <key>SupportedPlatform</key>
<string>ios</string> <string>ios</string>
<key>SupportedPlatformVariant</key>
<string>simulator</string>
</dict> </dict>
</array> </array>
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
......
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
</data> </data>
<key>Info.plist</key> <key>Info.plist</key>
<data> <data>
AaJE9H7xzNyQkDiLPw3sM9LfFjQ= w/Rc8R+JLx3x4AhZWV+3tG1CxCM=
</data> </data>
<key>Modules/module.modulemap</key> <key>Modules/module.modulemap</key>
<data> <data>
......
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
</data> </data>
<key>Info.plist</key> <key>Info.plist</key>
<data> <data>
RNXd5p+E7NvIdc7OKurgeIO55/s= bbhIYcGh7MnGD7V1rEns5GGhP1g=
</data> </data>
<key>Modules/module.modulemap</key> <key>Modules/module.modulemap</key>
<data> <data>
......
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