Commit 55bae677 authored by Wei Han's avatar Wei Han

code update

parent 22ca578e
...@@ -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 */; };
C23D2D55B148EE1D6FC72852 /* libPods-example.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E39174A7BBC525ECB6AE459E /* libPods-example.a */; }; 8B0987845C560FD781B650AE /* libPods-example.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 8A2400CB7299C3614291226C /* 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>"; };
690F9972D97F745507217F02 /* 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>"; }; 57196ABB0FAFEBD2B74D07C7 /* 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>"; };
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>"; };
B9F2E44A1B314258ABABFD33 /* 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>"; }; 8A2400CB7299C3614291226C /* libPods-example.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-example.a"; sourceTree = BUILT_PRODUCTS_DIR; };
E39174A7BBC525ECB6AE459E /* libPods-example.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-example.a"; sourceTree = BUILT_PRODUCTS_DIR; }; E8EAD6F76E99A3EACFCAFAA5 /* 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 = (
C23D2D55B148EE1D6FC72852 /* libPods-example.a in Frameworks */, 8B0987845C560FD781B650AE /* 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 */,
E39174A7BBC525ECB6AE459E /* libPods-example.a */, 8A2400CB7299C3614291226C /* 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 = (
B9F2E44A1B314258ABABFD33 /* Pods-example.debug.xcconfig */, 57196ABB0FAFEBD2B74D07C7 /* Pods-example.debug.xcconfig */,
690F9972D97F745507217F02 /* Pods-example.release.xcconfig */, E8EAD6F76E99A3EACFCAFAA5 /* 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 = (
890064CE3093270C24794A65 /* [CP] Check Pods Manifest.lock */, 345C81D5C27EE395225881EA /* [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 */,
7E9430E69C9A5F57AD35CC5A /* [CP] Embed Pods Frameworks */, 82A113167A34CAB220114EBD /* [CP] Embed Pods Frameworks */,
DB2FCD814B6946290F8F7B0A /* [CP] Copy Pods Resources */, 5366F480F3743B4AE3E10ADF /* [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";
}; };
7E9430E69C9A5F57AD35CC5A /* [CP] Embed Pods Frameworks */ = { 345C81D5C27EE395225881EA /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
890064CE3093270C24794A65 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
...@@ -222,7 +205,7 @@ ...@@ -222,7 +205,7 @@
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0; showEnvVarsInLog = 0;
}; };
DB2FCD814B6946290F8F7B0A /* [CP] Copy Pods Resources */ = { 5366F480F3743B4AE3E10ADF /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
...@@ -239,6 +222,23 @@ ...@@ -239,6 +222,23 @@
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-resources.sh\"\n"; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-resources.sh\"\n";
showEnvVarsInLog = 0; showEnvVarsInLog = 0;
}; };
82A113167A34CAB220114EBD /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-frameworks.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 = B9F2E44A1B314258ABABFD33 /* Pods-example.debug.xcconfig */; baseConfigurationReference = 57196ABB0FAFEBD2B74D07C7 /* 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 = 690F9972D97F745507217F02 /* Pods-example.release.xcconfig */; baseConfigurationReference = E8EAD6F76E99A3EACFCAFAA5 /* 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>
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
2FAEF09A2E8B7BC80086EDA3 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2FAEF0992E8B7BC80086EDA3 /* UIKit.framework */; }; 2FAEF09A2E8B7BC80086EDA3 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2FAEF0992E8B7BC80086EDA3 /* UIKit.framework */; };
2FAEF09C2E8B7BD40086EDA3 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2FAEF09B2E8B7BD40086EDA3 /* Foundation.framework */; }; 2FAEF09C2E8B7BD40086EDA3 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2FAEF09B2E8B7BD40086EDA3 /* Foundation.framework */; };
2FB2922A2EAB04F400D9F164 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2FB292292EAB04F400D9F164 /* AVFoundation.framework */; }; 2FB2922A2EAB04F400D9F164 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2FB292292EAB04F400D9F164 /* AVFoundation.framework */; };
2FB975E02ED7F9AA00E64A87 /* EventKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2FB975DF2ED7F9AA00E64A87 /* EventKit.framework */; };
2FD1557B2ECAC5CC00233981 /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2FD1557A2ECAC5CC00233981 /* WebKit.framework */; }; 2FD1557B2ECAC5CC00233981 /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2FD1557A2ECAC5CC00233981 /* WebKit.framework */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
...@@ -22,6 +23,7 @@ ...@@ -22,6 +23,7 @@
2FAEF0992E8B7BC80086EDA3 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.0.sdk/System/iOSSupport/System/Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; }; 2FAEF0992E8B7BC80086EDA3 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.0.sdk/System/iOSSupport/System/Library/Frameworks/UIKit.framework; sourceTree = DEVELOPER_DIR; };
2FAEF09B2E8B7BD40086EDA3 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.0.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; 2FAEF09B2E8B7BD40086EDA3 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.0.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; };
2FB292292EAB04F400D9F164 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.0.sdk/System/Library/Frameworks/AVFoundation.framework; sourceTree = DEVELOPER_DIR; }; 2FB292292EAB04F400D9F164 /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.0.sdk/System/Library/Frameworks/AVFoundation.framework; sourceTree = DEVELOPER_DIR; };
2FB975DF2ED7F9AA00E64A87 /* EventKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = EventKit.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.1.sdk/System/Library/Frameworks/EventKit.framework; sourceTree = DEVELOPER_DIR; };
2FD1557A2ECAC5CC00233981 /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.1.sdk/System/iOSSupport/System/Library/PrivateFrameworks/WebKit.framework; sourceTree = DEVELOPER_DIR; }; 2FD1557A2ECAC5CC00233981 /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.1.sdk/System/iOSSupport/System/Library/PrivateFrameworks/WebKit.framework; sourceTree = DEVELOPER_DIR; };
/* End PBXFileReference section */ /* End PBXFileReference section */
...@@ -52,6 +54,7 @@ ...@@ -52,6 +54,7 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
2FB975E02ED7F9AA00E64A87 /* EventKit.framework in Frameworks */,
2FB2922A2EAB04F400D9F164 /* AVFoundation.framework in Frameworks */, 2FB2922A2EAB04F400D9F164 /* AVFoundation.framework in Frameworks */,
2F99420D2EA9C5F000622199 /* QuartzCore.framework in Frameworks */, 2F99420D2EA9C5F000622199 /* QuartzCore.framework in Frameworks */,
2FD1557B2ECAC5CC00233981 /* WebKit.framework in Frameworks */, 2FD1557B2ECAC5CC00233981 /* WebKit.framework in Frameworks */,
...@@ -84,6 +87,7 @@ ...@@ -84,6 +87,7 @@
2FAEF0982E8B7BC80086EDA3 /* Frameworks */ = { 2FAEF0982E8B7BC80086EDA3 /* Frameworks */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
2FB975DF2ED7F9AA00E64A87 /* EventKit.framework */,
2FD1557A2ECAC5CC00233981 /* WebKit.framework */, 2FD1557A2ECAC5CC00233981 /* WebKit.framework */,
2FB292292EAB04F400D9F164 /* AVFoundation.framework */, 2FB292292EAB04F400D9F164 /* AVFoundation.framework */,
2F99420C2EA9C5F000622199 /* QuartzCore.framework */, 2F99420C2EA9C5F000622199 /* QuartzCore.framework */,
......
// AppointmentAPIClient.h
#import <Foundation/Foundation.h> #import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface AppointmentAPIClient : NSObject @interface AppointmentAPIClient : NSObject
+ (void)fetchAppointment:(void (^)(NSDictionary *data, NSError *error))completion; + (void)fetchAppointment:(void (^)(NSDictionary * _Nullable data, NSError * _Nullable error))completion;
+ (void)fetchGeneralInfo:(void (^)(NSDictionary *data, NSError *error))completion; + (void)fetchGeneralInfo:(void (^)(NSDictionary * _Nullable data, NSError * _Nullable error))completion;
+ (void)submitAppointmentWithData:(NSDictionary *)appointmentData + (void)submitAppointment:(NSDictionary *)appointmentData
completion:(void(^)(BOOL success, NSDictionary * _Nullable response, NSError * _Nullable error))completion; completion:(void(^)(BOOL success, NSDictionary * _Nullable response, NSError * _Nullable error))completion;
+ (void)submitRepresentativeAppointmentWithData:(NSDictionary *)appointmentData + (void)submitRepresentativeAppointment:(NSDictionary *)repData
representativeData:(NSDictionary *)repData
completion:(void(^)(BOOL success, NSDictionary * _Nullable response, NSError * _Nullable error))completion; completion:(void(^)(BOOL success, NSDictionary * _Nullable response, NSError * _Nullable error))completion;
+ (void)fetchCancelAppointment:(id)appointmentId + (void)fetchCancelAppointment:(id)appointmentId
reason:(NSString *)reason reason:(NSString *)reason
completion:(void (^)(NSDictionary *data, NSError *error))completion; completion:(void (^)(NSDictionary * _Nullable data, NSError * _Nullable error))completion;
@end @end
NS_ASSUME_NONNULL_END
...@@ -133,7 +133,7 @@ ...@@ -133,7 +133,7 @@
[task resume]; [task resume];
} }
+ (void)submitAppointmentWithData:(NSDictionary *)appointmentData + (void)submitAppointment:(NSDictionary *)appointmentData
completion:(void(^)(BOOL success, NSDictionary * _Nullable response, NSError * _Nullable error))completion { completion:(void(^)(BOOL success, NSDictionary * _Nullable response, NSError * _Nullable error))completion {
NSString *urlString = @"https://kitadev.commudesk.com/api/owner/appointment/makeAppointment?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6ImF0aGlyYWh6YWlkaUBjb252ZXAuY29tIiwicGFzc3dvcmQiOiIkMnkkMTAkNU9xYklqMnZ6UHJwckJrcVd5c3I2LmJCc1hVYS9Hd014eUhnL3RhUUhPUkNQcGdmTG8yakciLCJzdWIiOjIxNzAsImlzcyI6Imh0dHBzOi8va2l0YWRldi5jb21tdWRlc2suY29tL2FwaS9vd25lci9hdXRoL3Bhc3N3b3JkbGVzc19sb2dpbiIsImlhdCI6MTc2MDMxNTM1MiwiZXhwIjoyMDc1ODg0ODcyLCJuYmYiOjE3NjAzMTUzNTIsImp0aSI6IktoQmNyQnJEdDVNdDZlWmMifQ.JnxGbZ7hTeoOr6oibIzZMphgyKtTo2Aq9Efx6mrGfFA"; NSString *urlString = @"https://kitadev.commudesk.com/api/owner/appointment/makeAppointment?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6ImF0aGlyYWh6YWlkaUBjb252ZXAuY29tIiwicGFzc3dvcmQiOiIkMnkkMTAkNU9xYklqMnZ6UHJwckJrcVd5c3I2LmJCc1hVYS9Hd014eUhnL3RhUUhPUkNQcGdmTG8yakciLCJzdWIiOjIxNzAsImlzcyI6Imh0dHBzOi8va2l0YWRldi5jb21tdWRlc2suY29tL2FwaS9vd25lci9hdXRoL3Bhc3N3b3JkbGVzc19sb2dpbiIsImlhdCI6MTc2MDMxNTM1MiwiZXhwIjoyMDc1ODg0ODcyLCJuYmYiOjE3NjAzMTUzNTIsImp0aSI6IktoQmNyQnJEdDVNdDZlWmMifQ.JnxGbZ7hTeoOr6oibIzZMphgyKtTo2Aq9Efx6mrGfFA";
...@@ -193,57 +193,41 @@ ...@@ -193,57 +193,41 @@
[task resume]; [task resume];
} }
+ (void)submitRepresentativeAppointmentWithData:(NSDictionary *)appointmentData + (void)submitRepresentativeAppointment:(NSDictionary *)repData
representativeData:(NSDictionary *)repData
completion:(void(^)(BOOL success, NSDictionary * _Nullable response, NSError * _Nullable error))completion { completion:(void(^)(BOOL success, NSDictionary * _Nullable response, NSError * _Nullable error))completion {
NSString *urlString = @"https://kitadev.commudesk.com/api/owner/appointment/makeAppointment?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6ImF0aGlyYWh6YWlkaUBjb252ZXAuY29tIiwicGFzc3dvcmQiOiIkMnkkMTAkNU9xYklqMnZ6UHJwckJrcVd5c3I2LmJCc1hVYS9Hd014eUhnL3RhUUhPUkNQcGdmTG8yakciLCJzdWIiOjIxNzAsImlzcyI6Imh0dHBzOi8va2l0YWRldi5jb21tdWRlc2suY29tL2FwaS9vd25lci9hdXRoL3Bhc3N3b3JkbGVzc19sb2dpbiIsImlhdCI6MTc2MDMxNTM1MiwiZXhwIjoyMDc1ODg0ODcyLCJuYmYiOjE3NjAzMTUzNTIsImp0aSI6IktoQmNyQnJEdDVNdDZlWmMifQ.JnxGbZ7hTeoOr6oibIzZMphgyKtTo2Aq9Efx6mrGfFA"; NSString *urlString = @"https://kitadev.commudesk.com/api/owner/appointment/makeAppointment?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJlbWFpbCI6ImF0aGlyYWh6YWlkaUBjb252ZXAuY29tIiwicGFzc3dvcmQiOiIkMnkkMTAkNU9xYklqMnZ6UHJwckJrcVd5c3I2LmJCc1hVYS9Hd014eUhnL3RhUUhPUkNQcGdmTG8yakciLCJzdWIiOjIxNzAsImlzcyI6Imh0dHBzOi8va2l0YWRldi5jb21tdWRlc2suY29tL2FwaS9vd25lci9hdXRoL3Bhc3N3b3JkbGVzc19sb2dpbiIsImlhdCI6MTc2MDMxNTM1MiwiZXhwIjoyMDc1ODg0ODcyLCJuYmYiOjE3NjAzMTUzNTIsImp0aSI6IktoQmNyQnJEdDVNdDZlWmMifQ.JnxGbZ7hTeoOr6oibIzZMphgyKtTo2Aq9Efx6mrGfFA";
NSURL *url = [NSURL URLWithString:urlString]; NSURL *url = [NSURL URLWithString:urlString];
NSLog(@"🌍 SUBMIT URL: %@", urlString);
NSLog(@"🌍 SUBMIT (REP) URL: %@", urlString); // Create request
NSLog(@"📦 SUBMIT (REP) Appointment Data: %@", appointmentData);
NSLog(@"📎 SUBMIT (REP) Representative Data: %@", repData);
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url]; NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST"; request.HTTPMethod = @"POST";
// Multipart boundary
NSString *boundary = [NSString stringWithFormat:@"Boundary-%@", [[NSUUID UUID] UUIDString]]; NSString *boundary = [NSString stringWithFormat:@"Boundary-%@", [[NSUUID UUID] UUIDString]];
[request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary] [request setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@", boundary]
forHTTPHeaderField:@"Content-Type"]; forHTTPHeaderField:@"Content-Type"];
NSMutableData *body = [NSMutableData data]; NSMutableData *body = [NSMutableData data];
// Add appointment fields // Add repData fields as text fields
for (NSString *key in appointmentData) {
NSString *field = [self multipartFormField:key value:appointmentData[key] boundary:boundary];
[body appendData:[field dataUsingEncoding:NSUTF8StringEncoding]];
}
// Add representative fields
for (NSString *key in repData) { for (NSString *key in repData) {
id value = repData[key]; NSString *value = [NSString stringWithFormat:@"%@", repData[key]]; // ensure string
NSString *field = [NSString stringWithFormat:
if ([value isKindOfClass:[NSDictionary class]] && value[@"filePath"]) { @"--%@\r\nContent-Disposition: form-data; name=\"%@\"\r\n\r\n%@\r\n",
NSLog(@"📎 Adding file for key '%@': %@", key, value[@"fileName"]); boundary, key, value];
[body appendData:[self multipartFormFile:value[@"filePath"]
fieldName:key
fileName:value[@"fileName"]
mimeType:value[@"mimeType"]
boundary:boundary]];
} else {
NSString *field = [self multipartFormField:key value:value boundary:boundary];
[body appendData:[field dataUsingEncoding:NSUTF8StringEncoding]]; [body appendData:[field dataUsingEncoding:NSUTF8StringEncoding]];
} }
}
// Close multipart // Close multipart
[body appendData:[[NSString stringWithFormat:@"--%@--\r\n", boundary] [body appendData:[[NSString stringWithFormat:@"--%@--\r\n", boundary] dataUsingEncoding:NSUTF8StringEncoding]];
dataUsingEncoding:NSUTF8StringEncoding]];
request.HTTPBody = body; request.HTTPBody = body;
NSLog(@"🔄 SUBMIT (REP) Request started with multipart body (%lu bytes)", (unsigned long)body.length); NSLog(@"🔄 Request started with body (%lu bytes)", (unsigned long)body.length);
// Send request
NSURLSessionDataTask *task = [[NSURLSession sharedSession] NSURLSessionDataTask *task = [[NSURLSession sharedSession]
dataTaskWithRequest:request dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) { completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
...@@ -267,8 +251,7 @@ ...@@ -267,8 +251,7 @@
return; return;
} }
NSLog(@"✅ SUBMIT (REP) API Response:\n%@", json); NSLog(@"✅ API Response:\n%@", json);
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
completion(YES, json, nil); completion(YES, json, nil);
}); });
...@@ -333,7 +316,7 @@ ...@@ -333,7 +316,7 @@
if (jsonParseError) { if (jsonParseError) {
NSLog(@"⚠️ JSON Parse Error: %@", jsonParseError); NSLog(@"⚠️ JSON Parse Error: %@", jsonParseError);
} else { } else {
NSLog(@"✅ Void Issues API Response:\n%@", jsonResponse); NSLog(@"✅ Fetch Cancel Appointment Response:\n%@", jsonResponse);
} }
dispatch_async(dispatch_get_main_queue(), ^{ dispatch_async(dispatch_get_main_queue(), ^{
......
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
@property (nonatomic, strong) UIButton *repToggleButton; @property (nonatomic, strong) UIButton *repToggleButton;
@property (nonatomic, strong) UIView *repOverlayContainer; @property (nonatomic, strong) UIView *repOverlayContainer;
@property (nonatomic, strong) UIStackView *repInputsContainer; @property (nonatomic, strong) UIStackView *repInputsContainer;
@property (nonatomic, strong) NSString *repId;
@property (nonatomic, assign) BOOL blnTickRepresentative; @property (nonatomic, assign) BOOL blnTickRepresentative;
@property (nonatomic, strong) UIImageView *icPhotoView; @property (nonatomic, strong) UIImageView *icPhotoView;
@property (nonatomic, strong) UIImageView *letterPhotoView; @property (nonatomic, strong) UIImageView *letterPhotoView;
...@@ -485,11 +486,12 @@ ...@@ -485,11 +486,12 @@
#pragma mark - representative #pragma mark - representative
- (void)toggleRepresentative:(UIButton *)sender { - (void)toggleRepresentative:(UIButton *)sender {
if (self.repInputsContainer.isHidden) { if (self.repInputsContainer.isHidden) {
// First tap → show picker
[self showRepresentativePicker]; [self showRepresentativePicker];
self.blnTickRepresentative = YES;
} else { } else {
// Already showing → hide // Already showing → hide
[self hideRepresentativeInputs]; [self hideRepresentativeInputs];
self.blnTickRepresentative = NO;
} }
} }
...@@ -571,6 +573,7 @@ ...@@ -571,6 +573,7 @@
self.repICField.text = rep[@"ic_passport"] ?: @""; self.repICField.text = rep[@"ic_passport"] ?: @"";
self.repEmailField.text = rep[@"email"] ?: @""; self.repEmailField.text = rep[@"email"] ?: @"";
self.repContactField.text = rep[@"phone"] ?: @""; self.repContactField.text = rep[@"phone"] ?: @"";
self.repId = rep[@"id"] ?: @"";
self.icPhoto = rep[@"ic_photo"]; self.icPhoto = rep[@"ic_photo"];
self.letterPhoto = rep[@"letter"]; self.letterPhoto = rep[@"letter"];
[self loadImage:rep[@"ic_photo"] intoImageView:self.icPhotoView]; [self loadImage:rep[@"ic_photo"] intoImageView:self.icPhotoView];
...@@ -598,6 +601,7 @@ ...@@ -598,6 +601,7 @@
self.repICField.text = @""; self.repICField.text = @"";
self.repEmailField.text = @""; self.repEmailField.text = @"";
self.repContactField.text = @""; self.repContactField.text = @"";
self.repId = @"";
// Hide overlay // Hide overlay
[UIView animateWithDuration:0.25 animations:^{ [UIView animateWithDuration:0.25 animations:^{
...@@ -634,35 +638,89 @@ ...@@ -634,35 +638,89 @@
} }
-(void)confirmPressed { -(void)confirmPressed {
NSLog(@"⚡ Confirm pressed, blnTickRepresentative = %@", self.blnTickRepresentative ? @"YES" : @"NO");
if (self.blnTickRepresentative) {
NSDictionary *repData = @{
@"data[os]": @"AND",
@"data[project_id]": @"8",
@"data[appointment_type]": self.appointmentType,
@"data[drawing_plan_id]": @"135",
@"data[appointment_attendee_type]": @"representative",
@"data[slot_id]": self.slotId,
@"data[representative_id]": self.repId,
@"data[imei]": @"AND:2a2f13ff17b1cbb5",
};
[AppointmentAPIClient submitRepresentativeAppointment:repData completion:^(BOOL success, NSDictionary * _Nullable response, NSError * _Nullable error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (success) {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Success"
message:@"Your appointment was submitted successfully."
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *ok = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault
handler:^(UIAlertAction * _Nonnull action) {
// Navigate away or dismiss
[self.navigationController popViewControllerAnimated:YES];
}];
[alert addAction:ok];
[self presentViewController:alert animated:YES completion:nil];
} else {
NSString *errorMsg = error.localizedDescription ?: @"Failed to submit appointment.";
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Error"
message:errorMsg
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *ok = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault
handler:nil]; // stay on the page
[alert addAction:ok];
[self presentViewController:alert animated:YES completion:nil];
}
});
}];
} else {
NSDictionary *appointmentData = @{ NSDictionary *appointmentData = @{
@"project_id": @"8", @"project_id": @"8",
@"slot_id": self.slotId, @"slot_id": self.slotId,
@"appointment_type": self.appointmentType, @"appointment_type": self.appointmentType,
@"user_id": @"2170", @"user_id": @"2170",
@"appointment_attendee_type": @"representative", @"appointment_attendee_type": @"joint_owner",
@"drawing_plan_id": @"135", @"drawing_plan_id": @"135",
}; };
if (self.blnTickRepresentative) { [AppointmentAPIClient submitAppointment:appointmentData completion:^(BOOL success, NSDictionary * _Nullable response, NSError * _Nullable error) {
NSDictionary *repData = @{
@"representative_name": self.repNameField.text,
@"representative_ic_passport": self.repICField.text,
@"representative_email": self.repEmailField.text,
@"representative_contact": self.repContactField.text,
// files example
@"ic_photo": self.icPhoto,
@"representative_letter": self.letterPhoto,
};
[AppointmentAPIClient submitRepresentativeAppointmentWithData:appointmentData representativeData:repData completion:^(BOOL success, NSDictionary * _Nullable response, NSError * _Nullable error) { dispatch_async(dispatch_get_main_queue(), ^{
// handle response if (success) {
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Success"
message:@"Your appointment was submitted successfully."
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *ok = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault
handler:^(UIAlertAction * _Nonnull action) {
[self.navigationController popViewControllerAnimated:YES];
}]; }];
[alert addAction:ok];
[self presentViewController:alert animated:YES completion:nil];
} else { } else {
[AppointmentAPIClient submitAppointmentWithData:appointmentData completion:^(BOOL success, NSDictionary * _Nullable response, NSError * _Nullable error) { NSString *errorMsg = error.localizedDescription ?: @"Failed to submit appointment.";
// handle response UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Error"
}]; message:errorMsg
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *ok = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil];
[alert addAction:ok];
[self presentViewController:alert animated:YES completion:nil];
} }
});
}];
} }
}
- (void)setBlnTickRepresentative:(BOOL)blnTickRepresentative {
if (_blnTickRepresentative != blnTickRepresentative) {
NSLog(@"🔄 blnTickRepresentative changed: %@ -> %@", _blnTickRepresentative ? @"YES" : @"NO", blnTickRepresentative ? @"YES" : @"NO");
_blnTickRepresentative = blnTickRepresentative;
}
}
@end @end
...@@ -3,8 +3,9 @@ ...@@ -3,8 +3,9 @@
#import "AppointmentDetailsViewController.h" #import "AppointmentDetailsViewController.h"
#import "AppointmentAPIClient.h" #import "AppointmentAPIClient.h"
#import <WebKit/WebKit.h> #import <WebKit/WebKit.h>
#import <EventKit/EventKit.h>
@interface AppointmentDetailsViewController () @interface AppointmentDetailsViewController () <UIScrollViewDelegate>
// Fixed UI // Fixed UI
@property (nonatomic, strong) UIView *navBar; @property (nonatomic, strong) UIView *navBar;
...@@ -14,8 +15,8 @@ ...@@ -14,8 +15,8 @@
@property (nonatomic, strong) UIView *tabsBar; @property (nonatomic, strong) UIView *tabsBar;
@property (nonatomic, strong) UIButton *summaryBtn; @property (nonatomic, strong) UIButton *summaryBtn;
@property (nonatomic, strong) UIButton *ownerBtn; @property (nonatomic, strong) UIButton *ownerBtn;
@property (nonatomic, strong) NSString *floorPlanImage;
// Scrollable content for each tab
@property (nonatomic, strong) UIScrollView *summaryScrollView; @property (nonatomic, strong) UIScrollView *summaryScrollView;
@property (nonatomic, strong) UIView *summaryContentView; @property (nonatomic, strong) UIView *summaryContentView;
@property (nonatomic, strong) UIView *summaryCard; @property (nonatomic, strong) UIView *summaryCard;
...@@ -24,7 +25,9 @@ ...@@ -24,7 +25,9 @@
@property (nonatomic, strong) UILabel *dateField; @property (nonatomic, strong) UILabel *dateField;
@property (nonatomic, strong) UILabel *slotField; @property (nonatomic, strong) UILabel *slotField;
@property (nonatomic, strong) UILabel *locField; @property (nonatomic, strong) UILabel *locField;
@property (nonatomic, strong) UILabel *remarksField;
@property (nonatomic, strong) UILabel *termsField; @property (nonatomic, strong) UILabel *termsField;
@property (nonatomic, strong) UIStackView *remarksRow;
@property (nonatomic, strong) UIScrollView *ownerScrollView; @property (nonatomic, strong) UIScrollView *ownerScrollView;
@property (nonatomic, strong) UIView *ownerContentView; @property (nonatomic, strong) UIView *ownerContentView;
...@@ -40,13 +43,14 @@ ...@@ -40,13 +43,14 @@
[super viewDidLoad]; [super viewDidLoad];
self.view.backgroundColor = UIColor.whiteColor; self.view.backgroundColor = UIColor.whiteColor;
[self fetchGeneralInfo];
[self setupNavBar]; [self setupNavBar];
[self setupUnitInfoHeader]; [self setupUnitInfoHeader];
[self setupTabsBar]; [self setupTabsBar];
[self setupSummaryScrollView]; [self setupSummaryScrollView];
[self setupOwnerScrollView]; [self setupOwnerScrollView];
[self showSummary]; // default tab [self showSummary];
NSLog(@"📦 Received Appointment Data: %@", self.appointmentData); NSLog(@"📦 Received Appointment Data: %@", self.appointmentData);
NSString *text = [NSString stringWithFormat:@"%@", self.appointmentData]; NSString *text = [NSString stringWithFormat:@"%@", self.appointmentData];
...@@ -70,10 +74,12 @@ ...@@ -70,10 +74,12 @@
self.navBar.translatesAutoresizingMaskIntoConstraints = NO; self.navBar.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:self.navBar]; [self.view addSubview:self.navBar];
self.backButton = [UIButton buttonWithType:UIButtonTypeCustom]; self.backButton = [UIButton buttonWithType:UIButtonTypeSystem];
[self.backButton setImage:[UIImage imageNamed:@"icon_white_back"] forState:UIControlStateNormal]; self.backButton.tag = 1;
[self.backButton addTarget:self action:@selector(backTapped) forControlEvents:UIControlEventTouchUpInside];
self.backButton.translatesAutoresizingMaskIntoConstraints = NO; self.backButton.translatesAutoresizingMaskIntoConstraints = NO;
[self.backButton setImage:[UIImage systemImageNamed:@"chevron.left"] forState:UIControlStateNormal];
self.backButton.tintColor = UIColor.whiteColor;
[self.backButton addTarget:self action:@selector(backTapped) forControlEvents:UIControlEventTouchUpInside];
[self.navBar addSubview:self.backButton]; [self.navBar addSubview:self.backButton];
self.titleLabel = [[UILabel alloc] init]; self.titleLabel = [[UILabel alloc] init];
...@@ -128,15 +134,19 @@ ...@@ -128,15 +134,19 @@
[header addSubview:unitType]; [header addSubview:unitType];
UIImageView *floorPlan = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"placeholder_floorplan"]]; UIImageView *floorPlan = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"placeholder_floorplan"]];
floorPlan.tag = 101;
floorPlan.layer.cornerRadius = 8; floorPlan.layer.cornerRadius = 8;
floorPlan.layer.borderColor = [UIColor colorWithWhite:0.85 alpha:1].CGColor; floorPlan.layer.borderColor = [UIColor colorWithWhite:0.85 alpha:1].CGColor;
floorPlan.layer.borderWidth = 1; floorPlan.layer.borderWidth = 1;
floorPlan.clipsToBounds = YES; floorPlan.clipsToBounds = YES;
floorPlan.translatesAutoresizingMaskIntoConstraints = NO; floorPlan.translatesAutoresizingMaskIntoConstraints = NO;
floorPlan.userInteractionEnabled = YES;
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleFloorPlanTap:)];
[floorPlan addGestureRecognizer:tap];
[header addSubview:floorPlan]; [header addSubview:floorPlan];
[NSLayoutConstraint activateConstraints:@[ [NSLayoutConstraint activateConstraints:@[
[header.topAnchor constraintEqualToAnchor:self.navBar.bottomAnchor constant:16], [header.topAnchor constraintEqualToAnchor:self.navBar.bottomAnchor constant:30],
[header.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:16], [header.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:16],
[header.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:-16], [header.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:-16],
...@@ -144,10 +154,10 @@ ...@@ -144,10 +154,10 @@
[unitText.topAnchor constraintEqualToAnchor:header.topAnchor], [unitText.topAnchor constraintEqualToAnchor:header.topAnchor],
[unitText.leadingAnchor constraintEqualToAnchor:header.leadingAnchor], [unitText.leadingAnchor constraintEqualToAnchor:header.leadingAnchor],
[unitNumber.topAnchor constraintEqualToAnchor:unitText.bottomAnchor constant:2], [unitNumber.topAnchor constraintEqualToAnchor:unitText.bottomAnchor constant:10],
[unitNumber.leadingAnchor constraintEqualToAnchor:header.leadingAnchor], [unitNumber.leadingAnchor constraintEqualToAnchor:header.leadingAnchor],
[unitType.topAnchor constraintEqualToAnchor:unitNumber.bottomAnchor constant:2], [unitType.topAnchor constraintEqualToAnchor:unitNumber.bottomAnchor constant:10],
[unitType.leadingAnchor constraintEqualToAnchor:header.leadingAnchor], [unitType.leadingAnchor constraintEqualToAnchor:header.leadingAnchor],
[floorPlan.centerYAnchor constraintEqualToAnchor:header.centerYAnchor], [floorPlan.centerYAnchor constraintEqualToAnchor:header.centerYAnchor],
...@@ -155,12 +165,88 @@ ...@@ -155,12 +165,88 @@
[floorPlan.widthAnchor constraintEqualToConstant:120], [floorPlan.widthAnchor constraintEqualToConstant:120],
[floorPlan.heightAnchor constraintEqualToConstant:120], [floorPlan.heightAnchor constraintEqualToConstant:120],
[header.bottomAnchor constraintEqualToAnchor:unitType.bottomAnchor constant:20] [header.bottomAnchor constraintEqualToAnchor:unitType.bottomAnchor constant:40]
]]; ]];
self.unitInfoHeader = header; self.unitInfoHeader = header;
} }
- (void)loadImage:(NSString *)urlString into:(UIImageView *)imageView {
if (!urlString || (id)urlString == [NSNull null]) return;
NSURL *url = [NSURL URLWithString:urlString];
if (!url) return;
NSURLSessionDataTask *task =
[[NSURLSession sharedSession] dataTaskWithURL:url
completionHandler:^(NSData *data, NSURLResponse *res, NSError *err) {
if (err || !data) return;
UIImage *img = [UIImage imageWithData:data];
if (!img) return;
dispatch_async(dispatch_get_main_queue(), ^{
imageView.image = img;
});
}];
[task resume];
}
- (void)handleFloorPlanTap:(UITapGestureRecognizer *)sender {
UIImageView *tappedImageView = (UIImageView *)sender.view;
if (!tappedImageView.image) return;
UIView *overlay = [[UIView alloc] initWithFrame:self.view.bounds];
overlay.backgroundColor = [UIColor colorWithWhite:0 alpha:0.95];
overlay.alpha = 0;
overlay.tag = 900;
overlay.userInteractionEnabled = YES;
[self.view addSubview:overlay];
UIImageView *fullImageView = [[UIImageView alloc] initWithFrame:overlay.bounds];
fullImageView.contentMode = UIViewContentModeScaleAspectFit;
fullImageView.image = tappedImageView.image;
fullImageView.userInteractionEnabled = YES;
[overlay addSubview:fullImageView];
fullImageView.translatesAutoresizingMaskIntoConstraints = NO;
[NSLayoutConstraint activateConstraints:@[
[fullImageView.topAnchor constraintEqualToAnchor:overlay.topAnchor],
[fullImageView.leadingAnchor constraintEqualToAnchor:overlay.leadingAnchor],
[fullImageView.trailingAnchor constraintEqualToAnchor:overlay.trailingAnchor],
[fullImageView.bottomAnchor constraintEqualToAnchor:overlay.bottomAnchor]
]];
UIScrollView *scroll = [[UIScrollView alloc] initWithFrame:overlay.bounds];
scroll.minimumZoomScale = 1.0;
scroll.maximumZoomScale = 3.0;
scroll.delegate = self; // implement viewForZoomingInScrollView
[overlay addSubview:scroll];
[scroll addSubview:fullImageView];
UITapGestureRecognizer *dismissTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(closeFullImage)];
[overlay addGestureRecognizer:dismissTap];
[UIView animateWithDuration:0.25 animations:^{
overlay.alpha = 1;
}];
}
- (void)closeFullImage {
UIView *overlay = [self.view viewWithTag:900];
if (!overlay) return;
[UIView animateWithDuration:0.25 animations:^{
overlay.alpha = 0;
} completion:^(BOOL finished) {
[overlay removeFromSuperview];
}];
}
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
return scrollView.subviews.firstObject;
}
#pragma mark - Tabs #pragma mark - Tabs
- (void)setupTabsBar { - (void)setupTabsBar {
...@@ -204,6 +290,7 @@ ...@@ -204,6 +290,7 @@
#pragma mark - Summary ScrollView #pragma mark - Summary ScrollView
- (void)setupSummaryScrollView { - (void)setupSummaryScrollView {
// ---------- Scroll View ----------
self.summaryScrollView = [[UIScrollView alloc] init]; self.summaryScrollView = [[UIScrollView alloc] init];
self.summaryScrollView.translatesAutoresizingMaskIntoConstraints = NO; self.summaryScrollView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:self.summaryScrollView]; [self.view addSubview:self.summaryScrollView];
...@@ -225,7 +312,7 @@ ...@@ -225,7 +312,7 @@
[self.summaryContentView.widthAnchor constraintEqualToAnchor:self.summaryScrollView.widthAnchor], [self.summaryContentView.widthAnchor constraintEqualToAnchor:self.summaryScrollView.widthAnchor],
]]; ]];
// Summary card // ---------- Summary Card ----------
self.summaryCard = [[UIView alloc] init]; self.summaryCard = [[UIView alloc] init];
self.summaryCard.backgroundColor = UIColor.whiteColor; self.summaryCard.backgroundColor = UIColor.whiteColor;
self.summaryCard.layer.cornerRadius = 12; self.summaryCard.layer.cornerRadius = 12;
...@@ -234,61 +321,96 @@ ...@@ -234,61 +321,96 @@
self.summaryCard.translatesAutoresizingMaskIntoConstraints = NO; self.summaryCard.translatesAutoresizingMaskIntoConstraints = NO;
[self.summaryContentView addSubview:self.summaryCard]; [self.summaryContentView addSubview:self.summaryCard];
// Stack inside card
UIStackView *stack = [[UIStackView alloc] init];
stack.axis = UILayoutConstraintAxisVertical;
stack.alignment = UIStackViewAlignmentFill;
stack.spacing = 16;
stack.translatesAutoresizingMaskIntoConstraints = NO;
[self.summaryCard addSubview:stack];
// ---------- Title ----------
UILabel *titleLabel = [[UILabel alloc] init]; UILabel *titleLabel = [[UILabel alloc] init];
titleLabel.text = @"Appointment Info"; titleLabel.text = @"Appointment Info";
titleLabel.font = [UIFont boldSystemFontOfSize:16]; titleLabel.font = [UIFont boldSystemFontOfSize:16];
titleLabel.translatesAutoresizingMaskIntoConstraints = NO; [stack addArrangedSubview:titleLabel];
[self.summaryCard addSubview:titleLabel];
// ---------- Field Values ----------
UILabel *(^makeField)(NSString*, NSString*) = ^UILabel*(NSString *label, NSString *value) { UILabel *typeValue = [[UILabel alloc] init];
UILabel *field = [[UILabel alloc] init]; typeValue.text = self.appointmentData[@"appointment_type"];
field.numberOfLines = 0; typeValue.font = [UIFont systemFontOfSize:15];
field.font = [UIFont systemFontOfSize:15];
field.text = [NSString stringWithFormat:@"%@ %@", label, value]; UILabel *dateValue = [[UILabel alloc] init];
field.translatesAutoresizingMaskIntoConstraints = NO; dateValue.text = self.appointmentData[@"start_date"];
[self.summaryCard addSubview:field]; dateValue.font = [UIFont systemFontOfSize:15];
return field;
}; UILabel *slotValue = [[UILabel alloc] init];
slotValue.text = self.appointmentData[@"start_time"];
self.typeField = makeField(@"Type:", self.appointmentData[@"appointment_type"]); slotValue.font = [UIFont systemFontOfSize:15];
self.dateField = makeField(@"Date:", self.appointmentData[@"start_date"]);
self.slotField = makeField(@"Time Slot:", self.appointmentData[@"start_time"]); UILabel *locValue = [[UILabel alloc] init];
self.locField = makeField(@"Location:", self.appointmentData[@"location"]); //hmm wtf locValue.text = @"Ara Residence, 71800 Nilai,\nNegeri Sembilan";
self.termsField = makeField(@"Terms &\nConditions:", @"View Here"); locValue.font = [UIFont systemFontOfSize:15];
locValue.numberOfLines = 0;
// Add rows (left title, right value)
[stack addArrangedSubview:[self makeRowWithTitle:@"Type" value:typeValue]];
[stack addArrangedSubview:[self makeRowWithTitle:@"Date" value:dateValue]];
[stack addArrangedSubview:[self makeRowWithTitle:@"Time Slot" value:slotValue]];
[stack addArrangedSubview:[self makeRowWithTitle:@"Location" value:locValue]];
// ---------- Remarks (Collapsible) ----------
self.remarksField = [[UILabel alloc] init];
self.remarksField.font = [UIFont systemFontOfSize:15];
self.remarksField.numberOfLines = 0;
self.remarksRow = [self makeRowWithTitle:@"Remarks" value:self.remarksField];
self.remarksRow.hidden = YES; // default
[stack addArrangedSubview:self.remarksRow];
// ---------- Terms & Conditions ----------
UILabel *termsLabel = [[UILabel alloc] init];
termsLabel.text = @"Terms & Conditions :";
termsLabel.font = [UIFont systemFontOfSize:15];
termsLabel.numberOfLines = 2;
[termsLabel.widthAnchor constraintEqualToConstant:120].active = YES;
UIButton *viewButton = [UIButton buttonWithType:UIButtonTypeSystem];
[viewButton setTitle:@"View here" forState:UIControlStateNormal];
viewButton.titleLabel.font = [UIFont systemFontOfSize:15 weight:UIFontWeightSemibold];
[viewButton addTarget:self action:@selector(openTermsDrawer) forControlEvents:UIControlEventTouchUpInside];
UIStackView *termsRow = [[UIStackView alloc] initWithArrangedSubviews:@[termsLabel, viewButton]];
termsRow.axis = UILayoutConstraintAxisHorizontal;
termsRow.spacing = 8;
[stack addArrangedSubview:termsRow];
// ---------- Calendar Button ----------
UIButton *calendarBtn = [UIButton buttonWithType:UIButtonTypeSystem];
[calendarBtn setTitle:@"Add To Calendar" forState:UIControlStateNormal];
[calendarBtn setTitleColor:UIColor.whiteColor forState:UIControlStateNormal];
calendarBtn.backgroundColor = [UIColor systemBlueColor];
calendarBtn.layer.cornerRadius = 16;
calendarBtn.titleLabel.font = [UIFont systemFontOfSize:15 weight:UIFontWeightSemibold];
[calendarBtn.heightAnchor constraintEqualToConstant:44].active = YES;
[calendarBtn addTarget:self action:@selector(addToCalendarPressed) forControlEvents:UIControlEventTouchUpInside];
[stack addArrangedSubview:calendarBtn];
// ---------- Card Layout Constraints ----------
[NSLayoutConstraint activateConstraints:@[ [NSLayoutConstraint activateConstraints:@[
[self.summaryCard.topAnchor constraintEqualToAnchor:self.summaryContentView.topAnchor constant:20], [self.summaryCard.topAnchor constraintEqualToAnchor:self.summaryContentView.topAnchor constant:20],
[self.summaryCard.leadingAnchor constraintEqualToAnchor:self.summaryContentView.leadingAnchor constant:16], [self.summaryCard.leadingAnchor constraintEqualToAnchor:self.summaryContentView.leadingAnchor constant:16],
[self.summaryCard.trailingAnchor constraintEqualToAnchor:self.summaryContentView.trailingAnchor constant:-16], [self.summaryCard.trailingAnchor constraintEqualToAnchor:self.summaryContentView.trailingAnchor constant:-16],
[titleLabel.topAnchor constraintEqualToAnchor:self.summaryCard.topAnchor constant:16], [stack.topAnchor constraintEqualToAnchor:self.summaryCard.topAnchor constant:16],
[titleLabel.leadingAnchor constraintEqualToAnchor:self.summaryCard.leadingAnchor constant:16], [stack.leadingAnchor constraintEqualToAnchor:self.summaryCard.leadingAnchor constant:16],
[titleLabel.trailingAnchor constraintEqualToAnchor:self.summaryCard.trailingAnchor constant:-16], [stack.trailingAnchor constraintEqualToAnchor:self.summaryCard.trailingAnchor constant:-16],
[stack.bottomAnchor constraintEqualToAnchor:self.summaryCard.bottomAnchor constant:-16],
[self.typeField.topAnchor constraintEqualToAnchor:titleLabel.bottomAnchor constant:12],
[self.typeField.leadingAnchor constraintEqualToAnchor:self.summaryCard.leadingAnchor constant:16],
[self.typeField.trailingAnchor constraintEqualToAnchor:self.summaryCard.trailingAnchor constant:-16],
[self.dateField.topAnchor constraintEqualToAnchor:self.typeField.bottomAnchor constant:12],
[self.dateField.leadingAnchor constraintEqualToAnchor:self.summaryCard.leadingAnchor constant:16],
[self.dateField.trailingAnchor constraintEqualToAnchor:self.summaryCard.trailingAnchor constant:-16],
[self.slotField.topAnchor constraintEqualToAnchor:self.dateField.bottomAnchor constant:12],
[self.slotField.leadingAnchor constraintEqualToAnchor:self.summaryCard.leadingAnchor constant:16],
[self.slotField.trailingAnchor constraintEqualToAnchor:self.summaryCard.trailingAnchor constant:-16],
[self.locField.topAnchor constraintEqualToAnchor:self.slotField.bottomAnchor constant:12],
[self.locField.leadingAnchor constraintEqualToAnchor:self.summaryCard.leadingAnchor constant:16],
[self.locField.trailingAnchor constraintEqualToAnchor:self.summaryCard.trailingAnchor constant:-16],
[self.termsField.topAnchor constraintEqualToAnchor:self.locField.bottomAnchor constant:12],
[self.termsField.leadingAnchor constraintEqualToAnchor:self.summaryCard.leadingAnchor constant:16],
[self.termsField.trailingAnchor constraintEqualToAnchor:self.summaryCard.trailingAnchor constant:-16],
[self.summaryCard.bottomAnchor constraintEqualToAnchor:self.termsField.bottomAnchor constant:16]
]]; ]];
// Cancel button // ---------- Cancel Appointment Button ----------
self.summaryCancelBtn = [UIButton buttonWithType:UIButtonTypeCustom]; self.summaryCancelBtn = [UIButton buttonWithType:UIButtonTypeCustom];
[self.summaryCancelBtn setTitle:@"Cancel Appointment" forState:UIControlStateNormal]; [self.summaryCancelBtn setTitle:@"Cancel Appointment" forState:UIControlStateNormal];
self.summaryCancelBtn.backgroundColor = [UIColor systemRedColor]; self.summaryCancelBtn.backgroundColor = [UIColor systemRedColor];
...@@ -297,16 +419,201 @@ ...@@ -297,16 +419,201 @@
self.summaryCancelBtn.translatesAutoresizingMaskIntoConstraints = NO; self.summaryCancelBtn.translatesAutoresizingMaskIntoConstraints = NO;
[self.summaryContentView addSubview:self.summaryCancelBtn]; [self.summaryContentView addSubview:self.summaryCancelBtn];
// ---------- Status Logic (Hide + Expand Remarks) ----------
NSString *status = self.appointmentData[@"status"];
if ([status.lowercaseString isEqualToString:@"cancelled"]) {
self.summaryCancelBtn.hidden = YES;
self.remarksField.text = self.appointmentData[@"reason"];
self.remarksRow.hidden = NO;
}
// ---------- Cancel Button Constraints ----------
[NSLayoutConstraint activateConstraints:@[ [NSLayoutConstraint activateConstraints:@[
[self.summaryCancelBtn.topAnchor constraintEqualToAnchor:self.termsField.bottomAnchor constant:40], [self.summaryCancelBtn.topAnchor constraintEqualToAnchor:self.summaryCard.bottomAnchor constant:40],
[self.summaryCancelBtn.leadingAnchor constraintEqualToAnchor:self.summaryContentView.leadingAnchor constant:16], [self.summaryCancelBtn.leadingAnchor constraintEqualToAnchor:self.summaryContentView.leadingAnchor constant:16],
[self.summaryCancelBtn.trailingAnchor constraintEqualToAnchor:self.summaryContentView.trailingAnchor constant:-16], [self.summaryCancelBtn.trailingAnchor constraintEqualToAnchor:self.summaryContentView.trailingAnchor constant:-16],
[self.summaryCancelBtn.heightAnchor constraintEqualToConstant:55], [self.summaryCancelBtn.heightAnchor constraintEqualToConstant:55],
[self.summaryCancelBtn.bottomAnchor constraintEqualToAnchor:self.summaryContentView.bottomAnchor constant:-20] // important for scrolling [self.summaryCancelBtn.bottomAnchor constraintEqualToAnchor:self.summaryContentView.bottomAnchor constant:-20]
]]; ]];
} }
#pragma mark - Owner ScrollView - (UIStackView *)makeRowWithTitle:(NSString *)title value:(UIView *)valueView {
UILabel *titleLabel = [[UILabel alloc] init];
titleLabel.text = [NSString stringWithFormat:@"%@ :", title];
titleLabel.font = [UIFont systemFontOfSize:15];
titleLabel.textColor = UIColor.darkGrayColor;
titleLabel.numberOfLines = 1;
[titleLabel.widthAnchor constraintEqualToConstant:120].active = YES;
UIStackView *row = [[UIStackView alloc] initWithArrangedSubviews:@[titleLabel, valueView]];
row.axis = UILayoutConstraintAxisHorizontal;
row.spacing = 8;
row.alignment = UIStackViewAlignmentTop;
return row;
}
- (void)openTermsDrawer {
NSDictionary *tnc = self.appointmentData[@"appointment_tnc"];
NSString *htmlString = tnc[@"content"];
if (!htmlString || ![htmlString isKindOfClass:[NSString class]]) {
return;
}
[self showBottomDrawerWithHTML:htmlString];
}
- (void)showBottomDrawerWithHTML:(NSString *)html {
UIWindow *window = UIApplication.sharedApplication.keyWindow;
// Dimmed background
UIView *dimView = [[UIView alloc] initWithFrame:window.bounds];
dimView.backgroundColor = [UIColor colorWithWhite:0 alpha:0.5];
dimView.alpha = 0;
dimView.tag = 9991;
UITapGestureRecognizer *dismissTap =
[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(closeBottomDrawer)];
[dimView addGestureRecognizer:dismissTap];
[window addSubview:dimView];
// Drawer container
CGFloat height = window.frame.size.height * 0.80;
UIView *drawer = [[UIView alloc] initWithFrame:CGRectMake(0,
window.frame.size.height,
window.frame.size.width,
height)];
drawer.backgroundColor = UIColor.whiteColor;
drawer.layer.cornerRadius = 16;
drawer.clipsToBounds = YES;
drawer.tag = 9992;
[window addSubview:drawer];
// Header label
UILabel *header = [[UILabel alloc] initWithFrame:CGRectMake(16, 16, drawer.frame.size.width - 32, 28)];
header.text = @"Terms & Conditions";
header.font = [UIFont boldSystemFontOfSize:18];
[drawer addSubview:header];
// Close button
UIButton *closeBtn = [UIButton buttonWithType:UIButtonTypeSystem];
closeBtn.frame = CGRectMake(drawer.frame.size.width - 50, 12, 40, 34);
[closeBtn setTitle:@"✕" forState:UIControlStateNormal];
closeBtn.titleLabel.font = [UIFont boldSystemFontOfSize:22];
[closeBtn addTarget:self action:@selector(closeBottomDrawer) forControlEvents:UIControlEventTouchUpInside];
[drawer addSubview:closeBtn];
// WebView
WKWebView *webView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 60, drawer.frame.size.width, height - 60)];
NSString *styledHTML = [NSString stringWithFormat:
@"<html>"
"<head>"
"<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1.0\">"
"<style>"
"body { font-size: 14px; font-family: -apple-system; line-height: 1.5; padding: 12px; background-color: #F9F9F9; }"
"p { margin-bottom: 12px; }"
"</style>"
"</head>"
"<body>%@</body>"
"</html>",
html
];
[webView loadHTMLString:styledHTML baseURL:nil];
[drawer addSubview:webView];
// Animate up
[UIView animateWithDuration:0.3 animations:^{
dimView.alpha = 1;
drawer.frame = CGRectMake(0,
window.frame.size.height - height,
window.frame.size.width,
height);
}];
}
- (void)closeBottomDrawer {
UIWindow *window = UIApplication.sharedApplication.keyWindow;
UIView *dimView = [window viewWithTag:9991];
UIView *drawer = [window viewWithTag:9992];
if (!drawer || !dimView) return;
[UIView animateWithDuration:0.3 animations:^{
dimView.alpha = 0;
drawer.frame = CGRectMake(0,
window.frame.size.height,
drawer.frame.size.width,
drawer.frame.size.height);
} completion:^(BOOL finished) {
[drawer removeFromSuperview];
[dimView removeFromSuperview];
}];
}
- (void)addToCalendarPressed {
EKEventStore *eventStore = [[EKEventStore alloc] init];
// Request access to Calendar
[eventStore requestAccessToEntityType:EKEntityTypeEvent completion:^(BOOL granted, NSError * _Nullable error) {
if (granted) {
dispatch_async(dispatch_get_main_queue(), ^{
EKEvent *event = [EKEvent eventWithEventStore:eventStore];
// Set title
NSString *title = [NSString stringWithFormat:@"[%@] %@: %@.",
self.appointmentData[@"project_name"],
self.appointmentData[@"unit_no"],
self.appointmentData[@"appointment_type"]];
event.title = title;
// Set start & end dates
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.dateFormat = @"dd MMM yyyy hh:mm a";
NSString *startString = [NSString stringWithFormat:@"%@ %@",
self.appointmentData[@"start_date"],
self.appointmentData[@"start_time"]];
NSString *endString = [NSString stringWithFormat:@"%@ %@",
self.appointmentData[@"end_date"],
self.appointmentData[@"end_time"]];
event.startDate = [formatter dateFromString:startString];
event.endDate = [formatter dateFromString:endString];
// Add to default calendar
event.calendar = [eventStore defaultCalendarForNewEvents];
NSError *err = nil;
[eventStore saveEvent:event span:EKSpanThisEvent commit:YES error:&err];
if (err) {
NSLog(@"❌ Error saving event: %@", err);
} else {
NSLog(@"✅ Event saved successfully");
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Success"
message:@"Event added to Calendar"
preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]];
[self presentViewController:alert animated:YES completion:nil];
}
});
} else {
NSLog(@"❌ Calendar permission not granted: %@", error);
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"Permission Denied"
message:@"Enable Calendar access in Settings"
preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]];
[self presentViewController:alert animated:YES completion:nil];
});
}
}];
}
#pragma mark - Owner ScrollView (Dynamic Cards) #pragma mark - Owner ScrollView (Dynamic Cards)
- (void)setupOwnerScrollView { - (void)setupOwnerScrollView {
self.ownerScrollView = [[UIScrollView alloc] init]; self.ownerScrollView = [[UIScrollView alloc] init];
...@@ -331,47 +638,50 @@ ...@@ -331,47 +638,50 @@
[self.ownerContentView.widthAnchor constraintEqualToAnchor:self.ownerScrollView.widthAnchor], [self.ownerContentView.widthAnchor constraintEqualToAnchor:self.ownerScrollView.widthAnchor],
]]; ]];
UIView *previousCard = nil; UIView *previous = nil;
NSArray *primaryArray = self.appointmentData[@"primary_buyer"];
NSDictionary *primaryOwner = nil;
if ([primaryArray isKindOfClass:[NSArray class]] && primaryArray.count > 0) {
primaryOwner = primaryArray[0]; // ✅ take first element
}
NSArray *jointOwners = self.appointmentData[@"joint_buyer"];
// ---------------------------
// PRIMARY OWNER CARD
// ---------------------------
if (primaryOwner) {
UIView *primaryCard = [self createOwnerCard:@"Primary Owner"
owner:primaryOwner];
// Primary Owner [self.ownerContentView addSubview:primaryCard];
NSArray *primaryOwners = self.appointmentData[@"primary_buyer"];
for (NSDictionary *owner in primaryOwners) {
UIView *card = [self createOwnerCardWithData:owner isPrimary:YES];
[self.ownerContentView addSubview:card];
[NSLayoutConstraint activateConstraints:@[ [NSLayoutConstraint activateConstraints:@[
[card.leadingAnchor constraintEqualToAnchor:self.ownerContentView.leadingAnchor constant:16], [primaryCard.topAnchor constraintEqualToAnchor:self.ownerContentView.topAnchor constant:10],
[card.trailingAnchor constraintEqualToAnchor:self.ownerContentView.trailingAnchor constant:-16], [primaryCard.leadingAnchor constraintEqualToAnchor:self.ownerContentView.leadingAnchor constant:20],
[primaryCard.trailingAnchor constraintEqualToAnchor:self.ownerContentView.trailingAnchor constant:-20],
]]; ]];
if (previousCard) { previous = primaryCard;
[card.topAnchor constraintEqualToAnchor:previousCard.bottomAnchor constant:16].active = YES;
} else {
[card.topAnchor constraintEqualToAnchor:self.ownerContentView.topAnchor constant:20].active = YES;
}
previousCard = card;
} }
// Joint Owners // ---------------------------
NSArray *jointOwners = self.appointmentData[@"joint_buyer"]; // JOINT OWNERS COMBINED CARD
for (NSDictionary *owner in jointOwners) { // ---------------------------
UIView *card = [self createOwnerCardWithData:owner isPrimary:NO]; if (jointOwners.count > 0) {
[self.ownerContentView addSubview:card]; UIView *jointCard = [self createJointOwnersCard:jointOwners];
[self.ownerContentView addSubview:jointCard];
[NSLayoutConstraint activateConstraints:@[ [NSLayoutConstraint activateConstraints:@[
[card.leadingAnchor constraintEqualToAnchor:self.ownerContentView.leadingAnchor constant:16], [jointCard.topAnchor constraintEqualToAnchor:previous.bottomAnchor constant:20],
[card.trailingAnchor constraintEqualToAnchor:self.ownerContentView.trailingAnchor constant:-16], [jointCard.leadingAnchor constraintEqualToAnchor:self.ownerContentView.leadingAnchor constant:20],
[jointCard.trailingAnchor constraintEqualToAnchor:self.ownerContentView.trailingAnchor constant:-20],
]]; ]];
if (previousCard) { previous = jointCard;
[card.topAnchor constraintEqualToAnchor:previousCard.bottomAnchor constant:16].active = YES;
} else {
[card.topAnchor constraintEqualToAnchor:self.ownerContentView.topAnchor constant:20].active = YES;
}
previousCard = card;
} }
// Cancel button // ---------- Cancel Button ----------
self.ownerCancelBtn = [UIButton buttonWithType:UIButtonTypeCustom]; self.ownerCancelBtn = [UIButton buttonWithType:UIButtonTypeCustom];
[self.ownerCancelBtn setTitle:@"Cancel Appointment" forState:UIControlStateNormal]; [self.ownerCancelBtn setTitle:@"Cancel Appointment" forState:UIControlStateNormal];
self.ownerCancelBtn.backgroundColor = [UIColor systemRedColor]; self.ownerCancelBtn.backgroundColor = [UIColor systemRedColor];
...@@ -380,69 +690,162 @@ ...@@ -380,69 +690,162 @@
self.ownerCancelBtn.translatesAutoresizingMaskIntoConstraints = NO; self.ownerCancelBtn.translatesAutoresizingMaskIntoConstraints = NO;
[self.ownerContentView addSubview:self.ownerCancelBtn]; [self.ownerContentView addSubview:self.ownerCancelBtn];
NSString *status = self.appointmentData[@"status"];
if ([status.lowercaseString isEqualToString:@"cancelled"]) {
self.ownerCancelBtn.hidden = YES;
}
[NSLayoutConstraint activateConstraints:@[ [NSLayoutConstraint activateConstraints:@[
[self.ownerCancelBtn.topAnchor constraintEqualToAnchor:previousCard ? previousCard.bottomAnchor : self.ownerContentView.topAnchor constant:20], [self.ownerCancelBtn.topAnchor constraintEqualToAnchor:previous.bottomAnchor constant:40],
[self.ownerCancelBtn.leadingAnchor constraintEqualToAnchor:self.ownerContentView.leadingAnchor constant:16], [self.ownerCancelBtn.leadingAnchor constraintEqualToAnchor:self.ownerContentView.leadingAnchor constant:16],
[self.ownerCancelBtn.trailingAnchor constraintEqualToAnchor:self.ownerContentView.trailingAnchor constant:-16], [self.ownerCancelBtn.trailingAnchor constraintEqualToAnchor:self.ownerContentView.trailingAnchor constant:-16],
[self.ownerCancelBtn.heightAnchor constraintEqualToConstant:55], [self.ownerCancelBtn.heightAnchor constraintEqualToConstant:55],
[self.ownerCancelBtn.bottomAnchor constraintEqualToAnchor:self.ownerContentView.bottomAnchor constant:-20] [self.ownerCancelBtn.bottomAnchor constraintEqualToAnchor:self.ownerContentView.bottomAnchor constant:-20]
]]; ]];
} }
// Updated createOwnerCardWithData // Updated createOwnerCardWithData
- (UIView *)createOwnerCardWithData:(NSDictionary *)ownerData isPrimary:(BOOL)isPrimary { - (UIView *)createOwnerCard:(NSString *)title owner:(NSDictionary *)owner {
UIView *card = [[UIView alloc] init]; UIView *card = [[UIView alloc] init];
card.translatesAutoresizingMaskIntoConstraints = NO;
card.backgroundColor = UIColor.whiteColor; card.backgroundColor = UIColor.whiteColor;
card.layer.cornerRadius = 12; card.layer.cornerRadius = 10;
card.layer.borderColor = [UIColor colorWithWhite:0.9 alpha:1].CGColor; card.layer.borderColor = [UIColor blackColor].CGColor; // card outline
card.layer.borderWidth = 1; card.layer.borderWidth = 1;
card.translatesAutoresizingMaskIntoConstraints = NO; card.layer.shadowColor = [UIColor colorWithWhite:0 alpha:0.1].CGColor;
card.layer.shadowOpacity = 0.2;
card.layer.shadowOffset = CGSizeMake(0, 2);
UILabel *header = [[UILabel alloc] init];
header.text = title;
header.font = [UIFont boldSystemFontOfSize:16];
header.translatesAutoresizingMaskIntoConstraints = NO;
[card addSubview:header];
UILabel *nameLabel = [[UILabel alloc] init]; UILabel *name = [self makeFieldLabel:[NSString stringWithFormat:@"Name: %@", owner[@"name"]]];
nameLabel.text = [NSString stringWithFormat:@"Name: %@", ownerData[@"name"]]; UILabel *email = [self makeFieldLabel:[NSString stringWithFormat:@"Email: %@", owner[@"email"]]];
nameLabel.font = [UIFont boldSystemFontOfSize:16]; UILabel *contact = [self makeFieldLabel:[NSString stringWithFormat:@"Contact: %@", owner[@"contact"]]];
nameLabel.translatesAutoresizingMaskIntoConstraints = NO;
UILabel *secondLabel = [[UILabel alloc] init]; [card addSubview:name];
secondLabel.font = [UIFont systemFontOfSize:14]; [card addSubview:email];
secondLabel.textColor = UIColor.darkGrayColor; [card addSubview:contact];
secondLabel.translatesAutoresizingMaskIntoConstraints = NO;
UILabel *thirdLabel = [[UILabel alloc] init]; [NSLayoutConstraint activateConstraints:@[
thirdLabel.font = [UIFont systemFontOfSize:14]; [header.topAnchor constraintEqualToAnchor:card.topAnchor constant:16],
thirdLabel.textColor = UIColor.darkGrayColor; [header.leadingAnchor constraintEqualToAnchor:card.leadingAnchor constant:16],
thirdLabel.translatesAutoresizingMaskIntoConstraints = NO; [header.trailingAnchor constraintEqualToAnchor:card.trailingAnchor constant:-16],
if (isPrimary) { [name.topAnchor constraintEqualToAnchor:header.bottomAnchor constant:14],
secondLabel.text = [NSString stringWithFormat:@"NRIC/Passport: %@", ownerData[@"ic_passport"]]; [name.leadingAnchor constraintEqualToAnchor:card.leadingAnchor constant:16],
thirdLabel.text = [NSString stringWithFormat:@"Contact No: %@", ownerData[@"contact"]];
} else { [email.topAnchor constraintEqualToAnchor:name.bottomAnchor constant:6],
secondLabel.text = [NSString stringWithFormat:@"Email: %@", ownerData[@"email"]]; [email.leadingAnchor constraintEqualToAnchor:card.leadingAnchor constant:16],
thirdLabel.text = [NSString stringWithFormat:@"Contact No: %@", ownerData[@"contact"]];
[contact.topAnchor constraintEqualToAnchor:email.bottomAnchor constant:6],
[contact.leadingAnchor constraintEqualToAnchor:card.leadingAnchor constant:16],
[contact.bottomAnchor constraintEqualToAnchor:card.bottomAnchor constant:-16],
]];
return card;
} }
[card addSubview:nameLabel]; - (UIView *)createJointOwnersCard:(NSArray *)owners {
[card addSubview:secondLabel]; UIView *card = [[UIView alloc] init];
[card addSubview:thirdLabel]; card.translatesAutoresizingMaskIntoConstraints = NO;
card.backgroundColor = UIColor.whiteColor;
card.layer.cornerRadius = 10;
card.layer.borderColor = [UIColor blackColor].CGColor; // card outline
card.layer.borderWidth = 1;
card.layer.shadowColor = [UIColor colorWithWhite:0 alpha:0.1].CGColor;
card.layer.shadowOpacity = 0.2;
card.layer.shadowOffset = CGSizeMake(0, 2);
UILabel *header = [[UILabel alloc] init];
header.text = @"Joint Owners";
header.font = [UIFont boldSystemFontOfSize:16];
header.translatesAutoresizingMaskIntoConstraints = NO;
[card addSubview:header];
UIView *previous = header;
[NSLayoutConstraint activateConstraints:@[
[header.topAnchor constraintEqualToAnchor:card.topAnchor constant:16],
[header.leadingAnchor constraintEqualToAnchor:card.leadingAnchor constant:16],
]];
for (int i = 0; i < owners.count; i++) {
NSDictionary *owner = owners[i];
// Container for each owner
UIView *ownerContainer = [[UIView alloc] init];
ownerContainer.translatesAutoresizingMaskIntoConstraints = NO;
[card addSubview:ownerContainer];
UILabel *name = [self makeFieldLabel:[NSString stringWithFormat:@"Name: %@", owner[@"name"] ?: @""]];
UILabel *email = [self makeFieldLabel:[NSString stringWithFormat:@"Email: %@", owner[@"email"] ?: @""]];
UILabel *contact = [self makeFieldLabel:[NSString stringWithFormat:@"Contact: %@", owner[@"contact"] ?: @""]];
[ownerContainer addSubview:name];
[ownerContainer addSubview:email];
[ownerContainer addSubview:contact];
// Layout labels inside ownerContainer
[NSLayoutConstraint activateConstraints:@[ [NSLayoutConstraint activateConstraints:@[
[nameLabel.topAnchor constraintEqualToAnchor:card.topAnchor constant:16], [name.topAnchor constraintEqualToAnchor:ownerContainer.topAnchor],
[nameLabel.leadingAnchor constraintEqualToAnchor:card.leadingAnchor constant:16], [name.leadingAnchor constraintEqualToAnchor:ownerContainer.leadingAnchor constant:0],
[nameLabel.trailingAnchor constraintEqualToAnchor:card.trailingAnchor constant:-16],
[email.topAnchor constraintEqualToAnchor:name.bottomAnchor constant:4],
[secondLabel.topAnchor constraintEqualToAnchor:nameLabel.bottomAnchor constant:8], [email.leadingAnchor constraintEqualToAnchor:ownerContainer.leadingAnchor constant:0],
[secondLabel.leadingAnchor constraintEqualToAnchor:card.leadingAnchor constant:16],
[secondLabel.trailingAnchor constraintEqualToAnchor:card.trailingAnchor constant:-16], [contact.topAnchor constraintEqualToAnchor:email.bottomAnchor constant:4],
[contact.leadingAnchor constraintEqualToAnchor:ownerContainer.leadingAnchor constant:0],
[thirdLabel.topAnchor constraintEqualToAnchor:secondLabel.bottomAnchor constant:8], [contact.bottomAnchor constraintEqualToAnchor:ownerContainer.bottomAnchor constant:0]
[thirdLabel.leadingAnchor constraintEqualToAnchor:card.leadingAnchor constant:16], ]];
[thirdLabel.trailingAnchor constraintEqualToAnchor:card.trailingAnchor constant:-16],
[thirdLabel.bottomAnchor constraintEqualToAnchor:card.bottomAnchor constant:-16] // Layout ownerContainer inside card
[NSLayoutConstraint activateConstraints:@[
[ownerContainer.topAnchor constraintEqualToAnchor:previous.bottomAnchor constant:12],
[ownerContainer.leadingAnchor constraintEqualToAnchor:card.leadingAnchor constant:16],
[ownerContainer.trailingAnchor constraintEqualToAnchor:card.trailingAnchor constant:-16]
]];
previous = ownerContainer;
// Add separator line after each owner except the last one
if (i < owners.count - 1) {
UIView *separator = [[UIView alloc] init];
separator.backgroundColor = UIColor.blackColor;
separator.translatesAutoresizingMaskIntoConstraints = NO;
[card addSubview:separator];
[NSLayoutConstraint activateConstraints:@[
[separator.topAnchor constraintEqualToAnchor:previous.bottomAnchor constant:8],
[separator.leadingAnchor constraintEqualToAnchor:card.leadingAnchor constant:16],
[separator.trailingAnchor constraintEqualToAnchor:card.trailingAnchor constant:-16],
[separator.heightAnchor constraintEqualToConstant:1]
]];
previous = separator;
}
}
[NSLayoutConstraint activateConstraints:@[
[previous.bottomAnchor constraintEqualToAnchor:card.bottomAnchor constant:-16]
]]; ]];
return card; return card;
} }
- (UILabel *)makeFieldLabel:(NSString *)text {
UILabel *lbl = [[UILabel alloc] init];
lbl.font = [UIFont systemFontOfSize:14];
lbl.textColor = UIColor.darkGrayColor;
lbl.text = text;
lbl.translatesAutoresizingMaskIntoConstraints = NO;
return lbl;
}
#pragma mark - Tab Actions #pragma mark - Tab Actions
- (void)showSummary { - (void)showSummary {
self.summaryScrollView.hidden = NO; self.summaryScrollView.hidden = NO;
...@@ -460,7 +863,7 @@ ...@@ -460,7 +863,7 @@
#pragma mark - Actions #pragma mark - Actions
- (void)backTapped { - (void)backTapped {
[self.navigationController popViewControllerAnimated:YES]; [self dismissViewControllerAnimated:YES completion:nil];
} }
- (void)cancelPressed { - (void)cancelPressed {
...@@ -523,22 +926,66 @@ ...@@ -523,22 +926,66 @@
remarks = @"No reason provided"; remarks = @"No reason provided";
} }
[AppointmentAPIClient fetchCancelAppointment:self.appointmentID if (!self.appointmentData[@"id"]) {
NSLog(@"❌ Cannot cancel appointment: appointmentID is nil");
return;
}
[AppointmentAPIClient fetchCancelAppointment:self.appointmentData[@"id"]
reason:remarks reason:remarks
completion:^(NSDictionary *data, NSError *error) completion:^(NSDictionary *data, NSError *error)
{ {
dispatch_async(dispatch_get_main_queue(), ^{
NSString *title;
NSString *message;
if (error) { if (error) {
NSLog(@"❌ Cancel Error: %@", error.localizedDescription); title = @"Cancellation Failed";
return; message = error.localizedDescription ?: @"An unknown error occurred.";
} else {
title = @"Appointment Cancelled";
message = @"Your appointment has been successfully cancelled.";
} }
NSLog(@"✅ Appointment cancelled successfully."); UIAlertController *statusAlert =
[UIAlertController alertControllerWithTitle:title
message:message
preferredStyle:UIAlertControllerStyleAlert];
// Optional: close screen after cancel success [statusAlert addAction:[UIAlertAction actionWithTitle:@"OK"
dispatch_async(dispatch_get_main_queue(), ^{ style:UIAlertActionStyleDefault
handler:^(UIAlertAction * _Nonnull action) {
// Optional: dismiss this screen if you want after user taps OK
[self dismissViewControllerAnimated:YES completion:nil]; [self dismissViewControllerAnimated:YES completion:nil];
}]];
[self presentViewController:statusAlert animated:YES completion:nil];
}); });
}]; }];
} }
-(void) fetchGeneralInfo {
[AppointmentAPIClient fetchGeneralInfo:^(NSDictionary *data, NSError *error) {
if (error) return;
NSDictionary *generalInfo = data[@"Data"];
self.floorPlanImage = generalInfo[@"image"];
NSLog(@"self.floorPlanImage: %@",self.floorPlanImage);
dispatch_async(dispatch_get_main_queue(), ^{
[self updateFloorPlanImage];
});
}];
}
- (void)updateFloorPlanImage {
// Make sure the UIImageView exists
if (!self.unitInfoHeader) return;
UIImageView *floorPlan = [self.unitInfoHeader viewWithTag:101]; // use a tag
if (!floorPlan) return;
[self loadImage:self.floorPlanImage into:floorPlan];
}
@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