Commit 01266f51 authored by Wei Han's avatar Wei Han

code update

parent 7b821f42
nodeLinker: node-modules
nmHoistingLimits: workspaces
nodeLinker: node-modules
plugins:
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
spec: "@yarnpkg/plugin-interactive-tools"
......
......@@ -1779,93 +1779,6 @@ PODS:
- React-RCTFBReactNativeSpec
- ReactCommon/turbomodule/core
- SocketRocket
- react-native-safe-area-context (5.6.2):
- boost
- DoubleConversion
- fast_float
- fmt
- glog
- hermes-engine
- RCT-Folly
- RCT-Folly/Fabric
- RCTRequired
- RCTTypeSafety
- React-Core
- React-debug
- React-Fabric
- React-featureflags
- React-graphics
- React-ImageManager
- React-jsi
- react-native-safe-area-context/common (= 5.6.2)
- react-native-safe-area-context/fabric (= 5.6.2)
- React-NativeModulesApple
- React-RCTFabric
- React-renderercss
- React-rendererdebug
- React-utils
- ReactCodegen
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- SocketRocket
- Yoga
- react-native-safe-area-context/common (5.6.2):
- boost
- DoubleConversion
- fast_float
- fmt
- glog
- hermes-engine
- RCT-Folly
- RCT-Folly/Fabric
- RCTRequired
- RCTTypeSafety
- React-Core
- React-debug
- React-Fabric
- React-featureflags
- React-graphics
- React-ImageManager
- React-jsi
- React-NativeModulesApple
- React-RCTFabric
- React-renderercss
- React-rendererdebug
- React-utils
- ReactCodegen
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- SocketRocket
- Yoga
- react-native-safe-area-context/fabric (5.6.2):
- boost
- DoubleConversion
- fast_float
- fmt
- glog
- hermes-engine
- RCT-Folly
- RCT-Folly/Fabric
- RCTRequired
- RCTTypeSafety
- React-Core
- React-debug
- React-Fabric
- React-featureflags
- React-graphics
- React-ImageManager
- React-jsi
- react-native-safe-area-context/common
- React-NativeModulesApple
- React-RCTFabric
- React-renderercss
- React-rendererdebug
- React-utils
- ReactCodegen
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- SocketRocket
- Yoga
- React-NativeModulesApple (0.81.4):
- boost
- DoubleConversion
......@@ -2417,7 +2330,6 @@ DEPENDENCIES:
- React-logger (from `../node_modules/react-native/ReactCommon/logger`)
- React-Mapbuffer (from `../node_modules/react-native/ReactCommon`)
- React-microtasksnativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/microtasks`)
- react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`)
- React-NativeModulesApple (from `../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios`)
- React-oscompat (from `../node_modules/react-native/ReactCommon/oscompat`)
- React-perflogger (from `../node_modules/react-native/ReactCommon/reactperflogger`)
......@@ -2540,8 +2452,6 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/ReactCommon"
React-microtasksnativemodule:
:path: "../node_modules/react-native/ReactCommon/react/nativemodule/microtasks"
react-native-safe-area-context:
:path: "../node_modules/react-native-safe-area-context"
React-NativeModulesApple:
:path: "../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios"
React-oscompat:
......@@ -2648,7 +2558,6 @@ SPEC CHECKSUMS:
React-logger: 1767babce2d28c3251039ce05556714a2c8c6ded
React-Mapbuffer: 33f678ee25b6c0ee2b01b1ecec08e3e02424cefe
React-microtasksnativemodule: 44b44a4d3cd6ffb85d928abf741acdc26722de2e
react-native-safe-area-context: 54d812805f3c4e08a4580ad086cbde1d8780c2e4
React-NativeModulesApple: b5d18bc109c45c9a1c6b71664991b5cc3adc4e48
React-oscompat: 93b5535ea7f7dff46aaee4f78309a70979bdde9d
React-perflogger: a03d913e3205b00aee4128082abe42fd45ce0c98
......
......@@ -11,7 +11,7 @@
44006126336D83DB220E7214 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */; };
761780ED2CA45674006654EE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 761780EC2CA45674006654EE /* AppDelegate.swift */; };
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
AFC1B725D41173F56B62FA3B /* libPods-example.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F2A6CDBD96A29E1979EF0BD3 /* libPods-example.a */; };
A9347BA1DEB0869A7449D25A /* libPods-example.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 62D3056EB1529798F78D8AAE /* libPods-example.a */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
......@@ -19,12 +19,12 @@
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>"; };
13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = PrivacyInfo.xcprivacy; path = example/PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
5C8BADDA6881DBD3E3FFBD0E /* 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>"; };
62D3056EB1529798F78D8AAE /* libPods-example.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-example.a"; sourceTree = BUILT_PRODUCTS_DIR; };
761780EC2CA45674006654EE /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = example/AppDelegate.swift; sourceTree = "<group>"; };
81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = example/LaunchScreen.storyboard; sourceTree = "<group>"; };
9585DFA7130596921EB0F5A6 /* 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>"; };
C92BFB68E35543E7EF3282FC /* 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>"; };
9C044A90156FCEF85786783C /* 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>"; };
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
F2A6CDBD96A29E1979EF0BD3 /* libPods-example.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-example.a"; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
......@@ -32,7 +32,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
AFC1B725D41173F56B62FA3B /* libPods-example.a in Frameworks */,
A9347BA1DEB0869A7449D25A /* libPods-example.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
......@@ -55,7 +55,7 @@
isa = PBXGroup;
children = (
ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
F2A6CDBD96A29E1979EF0BD3 /* libPods-example.a */,
62D3056EB1529798F78D8AAE /* libPods-example.a */,
);
name = Frameworks;
sourceTree = "<group>";
......@@ -92,8 +92,8 @@
BBD78D7AC51CEA395F1C20DB /* Pods */ = {
isa = PBXGroup;
children = (
C92BFB68E35543E7EF3282FC /* Pods-example.debug.xcconfig */,
9585DFA7130596921EB0F5A6 /* Pods-example.release.xcconfig */,
9C044A90156FCEF85786783C /* Pods-example.debug.xcconfig */,
5C8BADDA6881DBD3E3FFBD0E /* Pods-example.release.xcconfig */,
);
path = Pods;
sourceTree = "<group>";
......@@ -105,13 +105,13 @@
isa = PBXNativeTarget;
buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "example" */;
buildPhases = (
94C2598371F476B4E1E60F33 /* [CP] Check Pods Manifest.lock */,
309150C9AAFDB30830CFACCC /* [CP] Check Pods Manifest.lock */,
13B07F871A680F5B00A75B9A /* Sources */,
13B07F8C1A680F5B00A75B9A /* Frameworks */,
13B07F8E1A680F5B00A75B9A /* Resources */,
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
97557091BD8C2C7B4B6F7AD4 /* [CP] Embed Pods Frameworks */,
052C3E3D9666B087C0F21867 /* [CP] Copy Pods Resources */,
82D720B7A901D1E9C13BE9D2 /* [CP] Embed Pods Frameworks */,
9C3136C3E0FC26587EC44C8F /* [CP] Copy Pods Resources */,
);
buildRules = (
);
......@@ -183,24 +183,7 @@
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";
};
052C3E3D9666B087C0F21867 /* [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;
};
94C2598371F476B4E1E60F33 /* [CP] Check Pods Manifest.lock */ = {
309150C9AAFDB30830CFACCC /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
......@@ -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";
showEnvVarsInLog = 0;
};
97557091BD8C2C7B4B6F7AD4 /* [CP] Embed Pods Frameworks */ = {
82D720B7A901D1E9C13BE9D2 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
......@@ -239,6 +222,23 @@
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
9C3136C3E0FC26587EC44C8F /* [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 */
/* Begin PBXSourcesBuildPhase section */
......@@ -255,7 +255,7 @@
/* Begin XCBuildConfiguration section */
13B07F941A680F5B00A75B9A /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = C92BFB68E35543E7EF3282FC /* Pods-example.debug.xcconfig */;
baseConfigurationReference = 9C044A90156FCEF85786783C /* Pods-example.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
......@@ -283,7 +283,7 @@
};
13B07F951A680F5B00A75B9A /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9585DFA7130596921EB0F5A6 /* Pods-example.release.xcconfig */;
baseConfigurationReference = 5C8BADDA6881DBD3E3FFBD0E /* Pods-example.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
......
......@@ -5,6 +5,8 @@
@interface AccessAppointmentViewController : UIViewController
@property (nonatomic, strong) NSDictionary *appointmentData;
@property (nonatomic, strong) NSString *unitID;
@property (nonatomic, strong) NSString *unitName;
@end
// AccessAppointmentViewController.mm
#import "AccessAppointmentViewController.h"
#import <EventKit/EventKit.h>
#import "AppointmentAPIClient.h"
@interface AccessAppointmentViewController ()
......@@ -17,6 +19,7 @@
@property (nonatomic, strong) UILabel *dateValueLabel;
@property (nonatomic, strong) UILabel *timeValueLabel;
@property (nonatomic, strong) UILabel *issuesLabel;
@property (nonatomic, strong) UILabel *issuesTitle;
// Buttons
@property (nonatomic, strong) UIButton *cancelButton;
......@@ -37,7 +40,6 @@
[self setupScrollView];
[self setupUnitSection];
[self setupAppointmentCard];
[self setupIssuesSection];
[self setupActionButtonsIfNeeded];
// Fill data
......@@ -151,47 +153,68 @@
self.cardView.layer.shadowOffset = CGSizeMake(0, 3);
[self.contentView addSubview:self.cardView];
// Title
// --- Title ---
UILabel *title = [[UILabel alloc] init];
title.translatesAutoresizingMaskIntoConstraints = NO;
title.text = @"Appointment Info";
title.font = [UIFont boldSystemFontOfSize:18];
[self.cardView addSubview:title];
// Type label + pill
// --- Type ---
UILabel *typeLabel = [self lightLabelWithText:@"Type :"];
[self.cardView addSubview:typeLabel];
self.typeValueLabel = [self pillLabel:@"Rectification"];
[self.cardView addSubview:self.typeValueLabel];
// Calendar button (right)
// --- Calendar button ---
self.calendarButton = [self calendarBoxButton];
[self.cardView addSubview:self.calendarButton];
// Date
// --- Date ---
UILabel *dateLabel = [self lightLabelWithText:@"Date :"];
[self.cardView addSubview:dateLabel];
self.dateValueLabel = [self regularLabelWithText:@"--"];
[self.cardView addSubview:self.dateValueLabel];
// Time
// --- Time ---
UILabel *timeLabel = [self lightLabelWithText:@"Time Slot :"];
[self.cardView addSubview:timeLabel];
self.timeValueLabel = [self regularLabelWithText:@"--"];
[self.cardView addSubview:self.timeValueLabel];
// Layout constraints
// --- Issues section (stack view) ---
self.issuesTitle = [[UILabel alloc] init];
self.issuesTitle.translatesAutoresizingMaskIntoConstraints = NO;
self.issuesTitle.text = @"Issue(s) Info";
self.issuesTitle.font = [UIFont boldSystemFontOfSize:18];
self.issuesLabel = [self regularLabelWithText:@"--"];
self.issuesLabel.translatesAutoresizingMaskIntoConstraints = NO;
self.issuesLabel.numberOfLines = 0;
UIStackView *issuesStack = [[UIStackView alloc] init];
issuesStack.translatesAutoresizingMaskIntoConstraints = NO;
issuesStack.axis = UILayoutConstraintAxisVertical;
issuesStack.spacing = 6;
[issuesStack addArrangedSubview:self.issuesTitle];
[issuesStack addArrangedSubview:self.issuesLabel];
[self.cardView addSubview:issuesStack];
// --- Layout constraints ---
[NSLayoutConstraint activateConstraints:@[
// Card position
[self.cardView.topAnchor constraintEqualToAnchor:self.unitLine.bottomAnchor constant:18],
[self.cardView.leadingAnchor constraintEqualToAnchor:self.contentView.leadingAnchor constant:16],
[self.cardView.trailingAnchor constraintEqualToAnchor:self.contentView.trailingAnchor constant:-16],
// Title
[title.topAnchor constraintEqualToAnchor:self.cardView.topAnchor constant:16],
[title.leadingAnchor constraintEqualToAnchor:self.cardView.leadingAnchor constant:16],
// Type
[typeLabel.topAnchor constraintEqualToAnchor:title.bottomAnchor constant:18],
[typeLabel.leadingAnchor constraintEqualToAnchor:self.cardView.leadingAnchor constant:16],
......@@ -203,55 +226,34 @@
[self.calendarButton.widthAnchor constraintEqualToConstant:92],
[self.calendarButton.heightAnchor constraintEqualToConstant:46],
// Date
[dateLabel.topAnchor constraintEqualToAnchor:typeLabel.bottomAnchor constant:18],
[dateLabel.leadingAnchor constraintEqualToAnchor:self.cardView.leadingAnchor constant:16],
[self.dateValueLabel.centerYAnchor constraintEqualToAnchor:dateLabel.centerYAnchor],
[self.dateValueLabel.leadingAnchor constraintEqualToAnchor:dateLabel.trailingAnchor constant:8],
// Time
[timeLabel.topAnchor constraintEqualToAnchor:dateLabel.bottomAnchor constant:14],
[timeLabel.leadingAnchor constraintEqualToAnchor:self.cardView.leadingAnchor constant:16],
[self.timeValueLabel.centerYAnchor constraintEqualToAnchor:timeLabel.centerYAnchor],
[self.timeValueLabel.leadingAnchor constraintEqualToAnchor:timeLabel.trailingAnchor constant:8],
// bottom anchor of card (provides contentView sizing)
[self.timeValueLabel.bottomAnchor constraintEqualToAnchor:self.cardView.bottomAnchor constant:-18]
]];
}
#pragma mark - Issues section
- (void)setupIssuesSection {
UILabel *title = [[UILabel alloc] init];
title.translatesAutoresizingMaskIntoConstraints = NO;
title.text = @"Issue(s) Info";
title.font = [UIFont boldSystemFontOfSize:18];
[self.contentView addSubview:title];
self.issuesLabel = [self regularLabelWithText:@"--"];
self.issuesLabel.translatesAutoresizingMaskIntoConstraints = NO;
self.issuesLabel.numberOfLines = 0;
[self.contentView addSubview:self.issuesLabel];
[NSLayoutConstraint activateConstraints:@[
[title.topAnchor constraintEqualToAnchor:self.cardView.bottomAnchor constant:20],
[title.leadingAnchor constraintEqualToAnchor:self.contentView.leadingAnchor constant:16],
[self.issuesLabel.topAnchor constraintEqualToAnchor:title.bottomAnchor constant:10],
[self.issuesLabel.leadingAnchor constraintEqualToAnchor:self.contentView.leadingAnchor constant:16],
[self.issuesLabel.trailingAnchor constraintEqualToAnchor:self.contentView.trailingAnchor constant:-16],
[self.issuesLabel.bottomAnchor constraintEqualToAnchor:self.contentView.bottomAnchor constant:-30]
// Issues stack
[issuesStack.topAnchor constraintEqualToAnchor:self.timeValueLabel.bottomAnchor constant:16],
[issuesStack.leadingAnchor constraintEqualToAnchor:self.cardView.leadingAnchor constant:16],
[issuesStack.trailingAnchor constraintEqualToAnchor:self.cardView.trailingAnchor constant:-16],
[issuesStack.bottomAnchor constraintEqualToAnchor:self.cardView.bottomAnchor constant:-16]
]];
}
#pragma mark - Action Buttons (Cancel + optional)
- (void)setupActionButtonsIfNeeded {
// If you always want to show cancel, set this to YES. Otherwise compute based on date/status.
BOOL shouldShowCancel = NO; // default: no
// If appointmentData includes a "can_cancel" key, respect it:
if (self.appointmentData[@"can_cancel"]) {
shouldShowCancel = [self.appointmentData[@"can_cancel"] boolValue];
}
NSString *status = self.appointmentData[@"appointment_status"];
// Only show cancel button if status is "Confirmed"
BOOL shouldShowCancel = [status isEqualToString:@"Confirmed"];
if (!shouldShowCancel) return;
......@@ -281,28 +283,44 @@
// appointmentData expected keys: unit_no, type, date, time_slot, issues
if (self.appointmentData == nil) return;
NSString *unitNo = self.appointmentData[@"unit_no"] ?: @"--";
NSString *unitNo = self.unitID ?: @"--";
self.unitLabel.text = unitNo;
NSString *type = self.appointmentData[@"type"] ?: @"Rectification";
self.typeValueLabel.text = type;
// date parsing: accepts yyyy-MM-dd or server format; fallback to raw string
NSString *rawDate = self.appointmentData[@"date"];
NSString *rawDate = self.appointmentData[@"appointment_date"];
if (rawDate) {
NSString *formatted = [self formattedDateStringFromRaw:rawDate];
self.dateValueLabel.text = formatted;
}
NSString *timeSlot = self.appointmentData[@"time_slot"] ?: self.appointmentData[@"time"] ?: @"--";
NSString *timeSlot = self.appointmentData[@"appointment_time"] ?: @"--";
self.timeValueLabel.text = timeSlot;
NSString *issues = self.appointmentData[@"issues"];
if (!issues && self.appointmentData[@"issue_list"]) {
NSArray *arr = self.appointmentData[@"issue_list"];
issues = [arr componentsJoinedByString:@"\n"];
NSArray *issuesArray = self.appointmentData[@"Issues"];
if (!issuesArray || issuesArray.count == 0) {
self.issuesTitle.hidden = YES;
self.issuesLabel.hidden = YES;
NSLog(@"Hiding Issues");
} else {
self.issuesTitle.hidden = NO;
self.issuesLabel.hidden = NO;
NSLog(@"Showing issues");
NSMutableArray *refs = [NSMutableArray array];
NSMutableArray *numLines = [NSMutableArray array];
NSInteger idx = 1;
for (NSDictionary *issueDict in issuesArray) {
NSString *ref = issueDict[@"reference"];
if (ref) {
[numLines addObject:[NSString stringWithFormat:@"%ld. %@", (long)idx, ref]];
idx++;
}
}
self.issuesLabel.text = [numLines componentsJoinedByString:@"\n"];
}
self.issuesLabel.text = issues ?: @"No issues specified";
}
#pragma mark - Helpers / UI helpers
......@@ -398,12 +416,59 @@
}
- (void)addToCalendarTapped {
// Hook: create event using EventKit if you want
UIAlertController *a = [UIAlertController alertControllerWithTitle:@"Add to Calendar"
message:@"Calendar integration not implemented yet."
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[@"appointment_date"],
self.appointmentData[@"appointment_time"]];
event.startDate = [formatter dateFromString:startString];
// 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];
[a addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]];
[self presentViewController:a animated:YES completion:nil];
[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];
});
}
}];
}
- (void)cancelPressed {
......@@ -437,24 +502,48 @@
}
- (void)sendCancellationRequestWithReason:(NSString *)remarks {
if (remarks.length == 0) remarks = @"No reason provided";
if (remarks.length == 0) {
remarks = @"No reason provided";
}
if (!self.appointmentData[@"id"]) {
NSLog(@"No appointment id, aborting cancellation");
NSLog(@"❌ Cannot cancel appointment: appointmentID is nil");
return;
}
// call API - placeholder
// [AppointmentAPIClient fetchCancelAppointment:self.appointmentData[@"id"] reason:remarks completion:...]
// For now simulate success:
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
UIAlertController *status = [UIAlertController alertControllerWithTitle:@"Appointment Cancelled"
message:@"Your appointment has been cancelled."
[AppointmentAPIClient fetchCancelAppointment:self.appointmentData[@"appointment_id"]
reason:remarks
completion:^(NSDictionary *data, NSError *error)
{
dispatch_async(dispatch_get_main_queue(), ^{
NSString *title;
NSString *message;
if (error) {
title = @"Cancellation Failed";
message = error.localizedDescription ?: @"An unknown error occurred.";
} else {
title = @"Appointment Cancelled";
message = @"Your appointment has been successfully cancelled.";
}
UIAlertController *statusAlert =
[UIAlertController alertControllerWithTitle:title
message:message
preferredStyle:UIAlertControllerStyleAlert];
[status addAction:[UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
[statusAlert addAction:[UIAlertAction actionWithTitle:@"OK"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * _Nonnull action) {
// Optional: dismiss this screen if you want after user taps OK
[self dismissViewControllerAnimated:YES completion:nil];
}]];
[self presentViewController:status animated:YES completion:nil];
[self presentViewController:statusAlert animated:YES completion:nil];
});
}];
}
@end
......@@ -434,6 +434,8 @@
if ([item[@"status"] isEqualToString:@"Rectification Appointment"]) {
AccessAppointmentViewController *vc = [[AccessAppointmentViewController alloc] init];
vc.appointmentData = item;
vc.unitID = self.unitID;
vc.unitName = self.unitName;
vc.modalPresentationStyle = UIModalPresentationFullScreen;
[self presentViewController:vc animated:YES completion:nil];
} else {
......
......@@ -193,32 +193,42 @@
}
- (UIView *)createTableHeader {
CGFloat headerHeight = 40;
NSArray *titles = @[@"No.", @"Item", @"Possession", @"Qty."];
CGFloat flexes[] = {0.7, 1.3, 1.3, 0.7}; // React Native style
CGFloat flexes[] = {0.7, 1.3, 1.3, 0.7};
CGFloat totalFlex = 0.7 + 1.3 + 1.3 + 0.7;
CGFloat spacing = 12;
UIView *header = [[UIView alloc] init];
header.backgroundColor = UIColor.blackColor;
header.translatesAutoresizingMaskIntoConstraints = NO;
UILabel *prev = nil;
CGFloat totalFlex = 0.7 + 1.3 + 1.3 + 0.7;
UIView *container = [[UIView alloc] init];
container.translatesAutoresizingMaskIntoConstraints = NO;
[header addSubview:container];
// Pin container edges to header
[NSLayoutConstraint activateConstraints:@[
[container.topAnchor constraintEqualToAnchor:header.topAnchor],
[container.leadingAnchor constraintEqualToAnchor:header.leadingAnchor],
[container.trailingAnchor constraintEqualToAnchor:header.trailingAnchor],
[container.bottomAnchor constraintEqualToAnchor:header.bottomAnchor]
]];
UILabel *prev = nil;
for (int i = 0; i < titles.count; i++) {
UILabel *lbl = [self createHeaderLabel:titles[i]];
[header addSubview:lbl];
lbl.translatesAutoresizingMaskIntoConstraints = NO;
[container addSubview:lbl];
[NSLayoutConstraint activateConstraints:@[
[lbl.topAnchor constraintEqualToAnchor:header.topAnchor constant:12],
[lbl.bottomAnchor constraintEqualToAnchor:header.bottomAnchor constant:-12],
[lbl.widthAnchor constraintEqualToAnchor:header.widthAnchor multiplier:flexes[i]/totalFlex]
[lbl.topAnchor constraintEqualToAnchor:container.topAnchor constant:spacing],
[lbl.bottomAnchor constraintEqualToAnchor:container.bottomAnchor constant:-spacing],
[lbl.widthAnchor constraintEqualToAnchor:container.widthAnchor multiplier:flexes[i]/totalFlex]
]];
if (prev) {
[lbl.leadingAnchor constraintEqualToAnchor:prev.trailingAnchor].active = YES;
} else {
[lbl.leadingAnchor constraintEqualToAnchor:header.leadingAnchor].active = YES;
[lbl.leadingAnchor constraintEqualToAnchor:container.leadingAnchor].active = YES;
}
prev = lbl;
......@@ -227,18 +237,22 @@
UIView *sep = [[UIView alloc] init];
sep.backgroundColor = [UIColor colorWithWhite:0.9 alpha:1.0];
sep.translatesAutoresizingMaskIntoConstraints = NO;
[header addSubview:sep];
[container addSubview:sep];
[NSLayoutConstraint activateConstraints:@[
[sep.widthAnchor constraintEqualToConstant:0.5],
[sep.topAnchor constraintEqualToAnchor:header.topAnchor constant:12],
[sep.bottomAnchor constraintEqualToAnchor:header.bottomAnchor constant:-12],
[sep.topAnchor constraintEqualToAnchor:container.topAnchor constant:spacing],
[sep.bottomAnchor constraintEqualToAnchor:container.bottomAnchor constant:-spacing],
[sep.leadingAnchor constraintEqualToAnchor:lbl.trailingAnchor]
]];
}
}
// Force intrinsic height
[header.heightAnchor constraintEqualToConstant:headerHeight].active = YES;
// Force header height dynamically
[header layoutIfNeeded];
CGFloat height = [header systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
header.frame = CGRectMake(0, 0, self.itemsTable.frame.size.width, height);
return header;
}
......@@ -342,6 +356,13 @@
submittedSignature.translatesAutoresizingMaskIntoConstraints = NO;
submittedSignature.contentMode = UIViewContentModeScaleAspectFit;
submittedSignature.clipsToBounds = YES;
submittedSignature.layer.borderColor = UIColor.blackColor.CGColor;
submittedSignature.layer.borderWidth = 1.0;
submittedSignature.layer.cornerRadius = 4.0; // optional rounded corners
submittedSignature.clipsToBounds = YES;
submittedSignature.userInteractionEnabled = YES;
UITapGestureRecognizer *tapSubmitted = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onTapSignature:)];
[submittedSignature addGestureRecognizer:tapSubmitted];
NSURL *submittedURL = [NSURL URLWithString:self.transactionData[@"signature_submit"]];
if (submittedURL) {
// Simple async load
......@@ -371,6 +392,13 @@
receivedSignature.translatesAutoresizingMaskIntoConstraints = NO;
receivedSignature.contentMode = UIViewContentModeScaleAspectFit;
receivedSignature.clipsToBounds = YES;
receivedSignature.layer.borderColor = UIColor.blackColor.CGColor;
receivedSignature.layer.borderWidth = 1.0;
receivedSignature.layer.cornerRadius = 4.0;
receivedSignature.clipsToBounds = YES;
receivedSignature.userInteractionEnabled = YES;
UITapGestureRecognizer *tapReceived = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onTapSignature:)];
[receivedSignature addGestureRecognizer:tapReceived];
NSURL *receivedURL = [NSURL URLWithString:self.transactionData[@"signature_receive"]];
if (receivedURL) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
......@@ -435,6 +463,44 @@
]];
}
- (void)onTapSignature:(UITapGestureRecognizer *)sender {
UIImageView *imgView = (UIImageView *)sender.view;
if (!imgView.image) return;
// Create full-screen viewer
UIViewController *vc = [[UIViewController alloc] init];
vc.view.backgroundColor = UIColor.blackColor;
UIScrollView *scroll = [[UIScrollView alloc] initWithFrame:vc.view.bounds];
scroll.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
scroll.minimumZoomScale = 1.0;
scroll.maximumZoomScale = 3.0;
scroll.delegate = (id<UIScrollViewDelegate>)self;
[vc.view addSubview:scroll];
UIImageView *zoomImage = [[UIImageView alloc] initWithImage:imgView.image];
zoomImage.frame = scroll.bounds;
zoomImage.contentMode = UIViewContentModeScaleAspectFit;
zoomImage.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[scroll addSubview:zoomImage];
// Tag to identify for zoom delegate
zoomImage.tag = 100;
UITapGestureRecognizer *dismissTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(dismissViewer)];
[vc.view addGestureRecognizer:dismissTap];
// Present full-screen
[self presentViewController:vc animated:YES completion:nil];
}
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
return [scrollView viewWithTag:100];
}
- (void)dismissViewer {
[self dismissViewControllerAnimated:YES completion:nil];
}
#pragma mark - Table
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
......
......@@ -566,7 +566,7 @@
NSString *title = [NSString stringWithFormat:@"[%@] %@: %@.",
self.appointmentData[@"project_name"],
self.appointmentData[@"unit_no"],
self.appointmentData[@"appointment_type"]];
self.appointmentData[@"status"]];
event.title = title;
// Set start & end dates
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -4038,11 +4038,11 @@ __metadata:
"QmsPlugin@file:../::locator=qms-plugin-example%40workspace%3Aexample":
version: 0.1.0
resolution: "QmsPlugin@file:../#../::hash=af21c5&locator=qms-plugin-example%40workspace%3Aexample"
resolution: "QmsPlugin@file:../#../::hash=0b253a&locator=qms-plugin-example%40workspace%3Aexample"
peerDependencies:
react: "*"
react-native: "*"
checksum: 62359d48e334f58417817ce08cbadbc903d955edfcec23fcd1f25e51a95155b0465958c08490b7333e72f4e1272fd5bd0678dd812a5805f8c5daeb7d6c891213
checksum: fe5001e87d7c2eb94dc2b5f47a53c81c9c7490a0a9f313c06aa692f505b29f6195b231247c92bfd7baa6b530ec534e88f12c379bcdbf69742ef3bf00a35ea729
languageName: node
linkType: hard
......
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