Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
W
weihan-plugin
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Wei Han
weihan-plugin
Commits
02bbb1d6
Commit
02bbb1d6
authored
Feb 03, 2026
by
Wei Han
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
code update
parent
227d7a6d
Show whitespace changes
Inline
Side-by-side
Showing
19 changed files
with
1215 additions
and
25 deletions
+1215
-25
Contents.json
...amework/Images.xcassets/star_empty.imageset/Contents.json
+21
-0
star_empty.png
...mework/Images.xcassets/star_empty.imageset/star_empty.png
+0
-0
Contents.json
...ramework/Images.xcassets/star_full.imageset/Contents.json
+21
-0
star_full.png
...ramework/Images.xcassets/star_full.imageset/star_full.png
+0
-0
DashboardViewController.h
...rk/QmsPluginFramework/Dashboard/DashboardViewController.h
+1
-0
DashboardViewController.mm
...k/QmsPluginFramework/Dashboard/DashboardViewController.mm
+120
-4
GeneralInfoViewController.h
.../QmsPluginFramework/Dashboard/GeneralInfoViewController.h
+1
-0
GeneralInfoViewController.mm
...QmsPluginFramework/Dashboard/GeneralInfoViewController.mm
+4
-18
UnitViewController.mm
...mework/QmsPluginFramework/Dashboard/UnitViewController.mm
+3
-0
OwnerAcceptanceViewController.h
...Framework/OwnerAcceptance/OwnerAcceptanceViewController.h
+14
-0
OwnerAcceptanceViewController.mm
...ramework/OwnerAcceptance/OwnerAcceptanceViewController.mm
+72
-0
POAViewController.h
...rk/QmsPluginFramework/OwnerAcceptance/POAViewController.h
+13
-0
POAViewController.mm
...k/QmsPluginFramework/OwnerAcceptance/POAViewController.mm
+70
-0
SinglePoaViewController.h
...PluginFramework/OwnerAcceptance/SinglePoaViewController.h
+15
-0
SinglePoaViewController.mm
...luginFramework/OwnerAcceptance/SinglePoaViewController.mm
+72
-0
SurveyViewController.h
...ramework/QmsPluginFramework/Survey/SurveyViewController.h
+9
-0
SurveyViewController.mm
...amework/QmsPluginFramework/Survey/SurveyViewController.mm
+703
-0
APIClient.h
...ginFramework/QmsPluginFramework/Utilities/API/APIClient.h
+2
-0
APIClient.mm
...inFramework/QmsPluginFramework/Utilities/API/APIClient.mm
+74
-3
No files found.
framework/QmsPluginFramework/Images.xcassets/star_empty.imageset/Contents.json
0 → 100644
View file @
02bbb1d6
{
"images"
:
[
{
"filename"
:
"star_empty.png"
,
"idiom"
:
"universal"
,
"scale"
:
"1x"
},
{
"idiom"
:
"universal"
,
"scale"
:
"2x"
},
{
"idiom"
:
"universal"
,
"scale"
:
"3x"
}
],
"info"
:
{
"author"
:
"xcode"
,
"version"
:
1
}
}
framework/QmsPluginFramework/Images.xcassets/star_empty.imageset/star_empty.png
0 → 100644
View file @
02bbb1d6
3.55 KB
framework/QmsPluginFramework/Images.xcassets/star_full.imageset/Contents.json
0 → 100644
View file @
02bbb1d6
{
"images"
:
[
{
"filename"
:
"star_full.png"
,
"idiom"
:
"universal"
,
"scale"
:
"1x"
},
{
"idiom"
:
"universal"
,
"scale"
:
"2x"
},
{
"idiom"
:
"universal"
,
"scale"
:
"3x"
}
],
"info"
:
{
"author"
:
"xcode"
,
"version"
:
1
}
}
framework/QmsPluginFramework/Images.xcassets/star_full.imageset/star_full.png
0 → 100644
View file @
02bbb1d6
2.35 KB
framework/QmsPluginFramework/QmsPluginFramework/Dashboard/DashboardViewController.h
View file @
02bbb1d6
...
...
@@ -17,6 +17,7 @@ NS_ASSUME_NONNULL_BEGIN
@property
(
nonatomic
,
copy
)
NSString
*
drawingPlanId
;
@property
(
nonatomic
,
copy
)
NSString
*
projectLogo
;
@property
(
nonatomic
,
copy
)
NSString
*
lotId
;
@property
(
nonatomic
,
assign
)
BOOL
canShowSurvey
;
@end
...
...
framework/QmsPluginFramework/QmsPluginFramework/Dashboard/DashboardViewController.mm
View file @
02bbb1d6
...
...
@@ -16,6 +16,7 @@
#import "APIConfig.h"
#import "ImageCacheHelper.h"
#import "ThirdPartyViewController.h"
#import "SurveyViewController.h"
@interface DashboardViewController () <UIScrollViewDelegate>
...
...
@@ -23,7 +24,6 @@
@property (nonatomic, strong) NSArray *issues;
@property (nonatomic, assign) BOOL hasPendingAccess;
@property (nonatomic, assign) BOOL hasSignOff;
@property (nonatomic, strong) NSString *strContactlessHandoverStatus;
@property (nonatomic, strong) NSString *strContactlessFormUrl;
@property (nonatomic, strong) NSString *strPendingAccessReminder;
@property (nonatomic, assign) BOOL bln_Block_Handover;
...
...
@@ -48,7 +48,10 @@
@property (nonatomic, strong) UIScrollView *scrollView;
@property (nonatomic, strong) UIView *contentView;
@property (nonatomic, strong) UIView *stickyBar;
@property (nonatomic, assign) BOOL hasSubmittedSurvey;
@property (nonatomic, assign) BOOL shouldShowSurveyBanner;
@property (nonatomic, assign) NSDictionary *generalInfo;
@property (nonatomic, strong) NSString *handoverStatus;
@end
...
...
@@ -94,6 +97,7 @@
- (void)refreshDashboardContent {
NSLog(@"🔁 Refreshing dashboard content...");
[self requestGeneralInfo];
dispatch_group_t group = dispatch_group_create();
__block NSDictionary *dashboardData = nil;
__block NSDictionary *announcementData = nil;
...
...
@@ -665,12 +669,19 @@
return;
}
NSNumber *surveySubmission = mainData[@"survey_submission"];
BOOL hasSubmittedSurvey = NO;
if (surveySubmission && surveySubmission.intValue == 1) {
hasSubmittedSurvey = YES;
}
self.hasSubmittedSurvey = hasSubmittedSurvey;
NSArray *appointments = mainData[@"appointment"] ?: @[];
NSArray *issues = mainData[@"issue_update"] ?: @[];
NSNumber *paCount = mainData[@"pa_count"];
NSNumber *poaCount = mainData[@"poa_count"];
NSString *accessMessage = mainData[@"access_item_message"];
NSString *handoverStatus = mainData[@"contactlessHandoverTempStatus"];
NSString *handoverURL = mainData[@"contactlessHandoverTempURL"];
BOOL hasPendingAccess = (paCount && paCount.intValue > 0);
...
...
@@ -710,7 +721,6 @@
self.bln_Block_Handover = blockHandover;
self.bln_Block_Inspection = blockInspection;
self.strPendingAccessReminder = accessMessage ?: @"";
self.strContactlessHandoverStatus = handoverStatus ?: @"";
self.strContactlessFormUrl = handoverURL ?: @"";
NSLog(@"✅ Dashboard parsed:");
...
...
@@ -773,6 +783,18 @@
[view removeFromSuperview];
}
// === Handover Survey ===
if ([self shouldDisplaySurvey]) {
NSLog(@"Displaying Survey");
UIView *surveyRow = [self buildSurveyRow];
[self.contentContainer addArrangedSubview:surveyRow];
[NSLayoutConstraint activateConstraints:@[
[surveyRow.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:16],
[surveyRow.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:-16],
]];
}
// === PROJECT UPDATES ===
NSArray *announcements = self.announcements;
if (announcements.count > 0) {
...
...
@@ -806,6 +828,24 @@
[self.contentContainer addArrangedSubview:bottomSpacer];
}
- (void)requestGeneralInfo{
[APIClient requestGeneralInfo:^(BOOL success, NSDictionary *data, NSError *error) {
if (error || !data) {
NSLog(@"❌ General info error: %@", error.localizedDescription);
return;
}
NSString *plainText = [NSString stringWithFormat:@"%@", data];
NSString *docsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
NSString *filePath = [docsPath stringByAppendingPathComponent:@"generalData.txt"];
[plainText writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:nil];
NSLog(@"📁 appointData saved as plain text to: %@", filePath);
self.handoverStatus = data[@"Data"][@"handover_status"];
self.generalInfo = data;
}];
}
#pragma mark - Announcement Section
- (UIView *)buildAnnouncementSection:(NSArray *)announcements {
UIView *section = [[UIView alloc] init];
...
...
@@ -1519,6 +1559,7 @@
nextVC.modalPresentationStyle = UIModalPresentationFullScreen;
nextVC.unitID = self.projectCode;
nextVC.unitName = self.projectName;
nextVC.data = self.generalInfo;
[self presentViewController:nextVC animated:YES completion:nil];
}
...
...
@@ -1657,4 +1698,79 @@
compatibleWithTraitCollection:nil];
}
- (BOOL)shouldDisplaySurvey {
BOOL handedOver =
[self.handoverStatus.lowercaseString isEqualToString:@"handed over"];
return self.canShowSurvey &&
!self.hasSubmittedSurvey &&
handedOver;
}
- (UIView *)buildSurveyRow {
UIView *row = [[UIView alloc] init];
row.translatesAutoresizingMaskIntoConstraints = NO;
row.backgroundColor =
[UIColor colorWithRed:255.0/255.0
green:100.0/255.0
blue:100.0/255.0
alpha:1.0];
row.layer.cornerRadius = 12;
row.clipsToBounds = YES;
[row.heightAnchor constraintEqualToConstant:56].active = YES;
// Tap
UITapGestureRecognizer *tap =
[[UITapGestureRecognizer alloc] initWithTarget:self
action:@selector(handleSurveyTap)];
[row addGestureRecognizer:tap];
// Icon
UIImageView *icon = [[UIImageView alloc]
initWithImage:[UIImage imageNamed:@"poa_icon_white"]];
icon.translatesAutoresizingMaskIntoConstraints = NO;
icon.contentMode = UIViewContentModeScaleAspectFit;
[row addSubview:icon];
// Title
UILabel *title = [[UILabel alloc] init];
title.translatesAutoresizingMaskIntoConstraints = NO;
title.text = @"Handover Survey";
title.font = [UIFont boldSystemFontOfSize:16];
title.textColor = UIColor.whiteColor;
[row addSubview:title];
// Chevron
UIImageView *arrow = [[UIImageView alloc]
initWithImage:[UIImage systemImageNamed:@"chevron.right"]];
arrow.translatesAutoresizingMaskIntoConstraints = NO;
arrow.tintColor = UIColor.whiteColor;
[row addSubview:arrow];
[NSLayoutConstraint activateConstraints:@[
[icon.leadingAnchor constraintEqualToAnchor:row.leadingAnchor constant:16],
[icon.centerYAnchor constraintEqualToAnchor:row.centerYAnchor],
[icon.widthAnchor constraintEqualToConstant:24],
[icon.heightAnchor constraintEqualToConstant:24],
[title.leadingAnchor constraintEqualToAnchor:icon.trailingAnchor constant:12],
[title.centerYAnchor constraintEqualToAnchor:row.centerYAnchor],
[arrow.trailingAnchor constraintEqualToAnchor:row.trailingAnchor constant:-16],
[arrow.centerYAnchor constraintEqualToAnchor:row.centerYAnchor],
[arrow.widthAnchor constraintEqualToConstant:16],
[arrow.heightAnchor constraintEqualToConstant:16],
]];
return row;
}
- (void)handleSurveyTap {
NSLog(@"📝 Survey tapped");
SurveyViewController *vc = [[SurveyViewController alloc] init];
vc.modalPresentationStyle = UIModalPresentationFullScreen;
[self presentViewController:vc animated:YES completion:nil];
}
@end
framework/QmsPluginFramework/QmsPluginFramework/Dashboard/GeneralInfoViewController.h
View file @
02bbb1d6
...
...
@@ -6,5 +6,6 @@
@property
(
nonatomic
,
strong
)
NSString
*
unitID
;
@property
(
nonatomic
,
strong
)
NSString
*
unitName
;
@property
(
nonatomic
,
strong
)
NSDictionary
*
data
;
@end
framework/QmsPluginFramework/QmsPluginFramework/Dashboard/GeneralInfoViewController.mm
View file @
02bbb1d6
...
...
@@ -85,7 +85,7 @@ typedef struct {
self.documentsContainer.clipsToBounds = YES;
self.formsContainer.clipsToBounds = YES;
[self
request
GeneralInfo];
[self
handle
GeneralInfo];
[self setupTopBar];
[self setupTabControl];
[self setupScrollView];
...
...
@@ -542,22 +542,9 @@ typedef struct {
}
#pragma mark - api
- (void)requestGeneralInfo {
[APIClient requestGeneralInfo:^(BOOL success, NSDictionary *data, NSError *error) {
if (error || !data) {
NSLog(@"❌ General info error: %@", error.localizedDescription);
return;
}
NSLog(@"📦 Received General Info Data: %@", data);
NSDictionary *general = data[@"Data"];
// 1️⃣ Write to file for debugging (your existing logic)
NSString *plainText = [NSString stringWithFormat:@"%@", data];
NSString *docsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
NSString *filePath = [docsPath stringByAppendingPathComponent:@"generalData.txt"];
[plainText writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:nil];
NSLog(@"📁 appointData saved as plain text to: %@", filePath);
- (void)handleGeneralInfo {
NSLog(@"📦 Received General Info Data: %@", self.data);
NSDictionary *general = self.data[@"Data"];
// 2️⃣ Safely extract each field
self.owners = general[@"unit_owner"] ?: @[];
...
...
@@ -602,7 +589,6 @@ typedef struct {
dispatch_async(dispatch_get_main_queue(), ^{
[self refreshUI];
});
}];
}
- (void)refreshUI {
...
...
framework/QmsPluginFramework/QmsPluginFramework/Dashboard/UnitViewController.mm
View file @
02bbb1d6
...
...
@@ -276,6 +276,8 @@ didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *drawingPlanId = [unit[@"id"] description];
[APIConfig setDrawingPlanId:drawingPlanId];
NSLog(@"lotid: %@", unit[@"lot_id"]);
NSNumber *ownerSurvey = unit[@"owner_survey"];
BOOL canShowSurvey = (ownerSurvey != nil && ownerSurvey.intValue == 1);
NSLog(@"self.projectLogo: %@", self.projectLogo);
DashboardViewController *vc = [[DashboardViewController alloc] init];
...
...
@@ -286,6 +288,7 @@ didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
vc.drawerController = self.drawerController;
vc.projectLogo = self.projectLogo;
vc.lotId = unit[@"lot_id"] ?: @"";
vc.canShowSurvey = canShowSurvey;
vc.modalPresentationStyle = UIModalPresentationFullScreen;
[self.navigationController pushViewController:vc animated:YES];
}
...
...
framework/QmsPluginFramework/QmsPluginFramework/OwnerAcceptance/OwnerAcceptanceViewController.h
0 → 100644
View file @
02bbb1d6
// OwnerAcceptanceViewController.h
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface
OwnerAcceptanceViewController
:
UIViewController
@end
NS_ASSUME_NONNULL_END
framework/QmsPluginFramework/QmsPluginFramework/OwnerAcceptance/OwnerAcceptanceViewController.mm
0 → 100644
View file @
02bbb1d6
// OwnerAcceptanceViewController.mm
#import "APIClient.h"
#import "OwnerAcceptanceViewController.h"
@interface OwnerAcceptanceViewController ()
// Header
@property (nonatomic, strong) UIView *headerBar;
@property (nonatomic, strong) UILabel *titleLabel;
@property (nonatomic, strong) UIButton *backButton;
@end
@implementation OwnerAcceptanceViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = UIColor.whiteColor;
[self setupHeader];
}
#pragma mark - Header
- (void)setupHeader {
UILayoutGuide *safe = self.view.safeAreaLayoutGuide;
self.headerBar = [[UIView alloc] init];
self.headerBar.backgroundColor = [UIColor colorWithRed:0.0 green:0.43 blue:0.55 alpha:1.0];
self.headerBar.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:self.headerBar];
[NSLayoutConstraint activateConstraints:@[
[self.headerBar.topAnchor constraintEqualToAnchor:safe.topAnchor],
[self.headerBar.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
[self.headerBar.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
[self.headerBar.heightAnchor constraintEqualToConstant:44],
]];
self.titleLabel = [[UILabel alloc] init];
self.titleLabel.text = @"Handover Survey";
self.titleLabel.font = [UIFont systemFontOfSize:18 weight:UIFontWeightMedium];
self.titleLabel.textColor = UIColor.whiteColor;
self.titleLabel.textAlignment = NSTextAlignmentCenter;
self.backButton = [UIButton buttonWithType:UIButtonTypeSystem];
[self.backButton setImage:[UIImage systemImageNamed:@"chevron.backward"] forState:UIControlStateNormal];
self.backButton.tintColor = UIColor.whiteColor;
[self.backButton addTarget:self action:@selector(onBack) forControlEvents:UIControlEventTouchUpInside];
for (UIView *sub in @[self.titleLabel, self.backButton]) {
sub.translatesAutoresizingMaskIntoConstraints = NO;
[self.headerBar addSubview:sub];
}
[NSLayoutConstraint activateConstraints:@[
[self.backButton.leadingAnchor constraintEqualToAnchor:self.headerBar.leadingAnchor constant:16],
[self.backButton.centerYAnchor constraintEqualToAnchor:self.headerBar.centerYAnchor],
[self.backButton.widthAnchor constraintEqualToConstant:24],
[self.backButton.heightAnchor constraintEqualToConstant:24],
[self.titleLabel.centerXAnchor constraintEqualToAnchor:self.headerBar.centerXAnchor],
[self.titleLabel.centerYAnchor constraintEqualToAnchor:self.headerBar.centerYAnchor],
]];
}
- (void)onBack {
[self dismissViewControllerAnimated:YES completion:nil];
}
@end
framework/QmsPluginFramework/QmsPluginFramework/OwnerAcceptance/POAViewController.h
0 → 100644
View file @
02bbb1d6
// POAViewController.h
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface
POAViewController
:
UIViewController
@end
NS_ASSUME_NONNULL_END
framework/QmsPluginFramework/QmsPluginFramework/OwnerAcceptance/POAViewController.mm
0 → 100644
View file @
02bbb1d6
// POAViewController.mm
#import "APIClient.h"
#import "POAViewController.h"
@interface POAViewController ()
// Header
@property (nonatomic, strong) UIView *headerBar;
@property (nonatomic, strong) UILabel *titleLabel;
@property (nonatomic, strong) UIButton *backButton;
@end
@implementation POAViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = UIColor.whiteColor;
[self setupHeader];
}
#pragma mark - Header
- (void)setupHeader {
UILayoutGuide *safe = self.view.safeAreaLayoutGuide;
self.headerBar = [[UIView alloc] init];
self.headerBar.backgroundColor = [UIColor colorWithRed:0.0 green:0.43 blue:0.55 alpha:1.0];
self.headerBar.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:self.headerBar];
[NSLayoutConstraint activateConstraints:@[
[self.headerBar.topAnchor constraintEqualToAnchor:safe.topAnchor],
[self.headerBar.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
[self.headerBar.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
[self.headerBar.heightAnchor constraintEqualToConstant:44],
]];
self.titleLabel = [[UILabel alloc] init];
self.titleLabel.text = @"Handover Survey";
self.titleLabel.font = [UIFont systemFontOfSize:18 weight:UIFontWeightMedium];
self.titleLabel.textColor = UIColor.whiteColor;
self.titleLabel.textAlignment = NSTextAlignmentCenter;
self.backButton = [UIButton buttonWithType:UIButtonTypeSystem];
[self.backButton setImage:[UIImage systemImageNamed:@"chevron.backward"] forState:UIControlStateNormal];
self.backButton.tintColor = UIColor.whiteColor;
[self.backButton addTarget:self action:@selector(onBack) forControlEvents:UIControlEventTouchUpInside];
for (UIView *sub in @[self.titleLabel, self.backButton]) {
sub.translatesAutoresizingMaskIntoConstraints = NO;
[self.headerBar addSubview:sub];
}
[NSLayoutConstraint activateConstraints:@[
[self.backButton.leadingAnchor constraintEqualToAnchor:self.headerBar.leadingAnchor constant:16],
[self.backButton.centerYAnchor constraintEqualToAnchor:self.headerBar.centerYAnchor],
[self.backButton.widthAnchor constraintEqualToConstant:24],
[self.backButton.heightAnchor constraintEqualToConstant:24],
[self.titleLabel.centerXAnchor constraintEqualToAnchor:self.headerBar.centerXAnchor],
[self.titleLabel.centerYAnchor constraintEqualToAnchor:self.headerBar.centerYAnchor],
]];
}
- (void)onBack {
[self dismissViewControllerAnimated:YES completion:nil];
}
@end
framework/QmsPluginFramework/QmsPluginFramework/OwnerAcceptance/SinglePoaViewController.h
0 → 100644
View file @
02bbb1d6
//
// SinglePoaViewController.h
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface
SinglePOAViewController
:
UIViewController
@end
NS_ASSUME_NONNULL_END
framework/QmsPluginFramework/QmsPluginFramework/OwnerAcceptance/SinglePoaViewController.mm
0 → 100644
View file @
02bbb1d6
// SinglePoaViewController.mm
#import "APIClient.h"
#import "SinglePOAViewController.h"
@interface SinglePOAViewController ()
// Header
@property (nonatomic, strong) UIView *headerBar;
@property (nonatomic, strong) UILabel *titleLabel;
@property (nonatomic, strong) UIButton *backButton;
@end
@implementation SinglePOAViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = UIColor.whiteColor;
[self setupHeader];
}
#pragma mark - Header
- (void)setupHeader {
UILayoutGuide *safe = self.view.safeAreaLayoutGuide;
self.headerBar = [[UIView alloc] init];
self.headerBar.backgroundColor = [UIColor colorWithRed:0.0 green:0.43 blue:0.55 alpha:1.0];
self.headerBar.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:self.headerBar];
[NSLayoutConstraint activateConstraints:@[
[self.headerBar.topAnchor constraintEqualToAnchor:safe.topAnchor],
[self.headerBar.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
[self.headerBar.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
[self.headerBar.heightAnchor constraintEqualToConstant:44],
]];
self.titleLabel = [[UILabel alloc] init];
self.titleLabel.text = @"Handover Survey";
self.titleLabel.font = [UIFont systemFontOfSize:18 weight:UIFontWeightMedium];
self.titleLabel.textColor = UIColor.whiteColor;
self.titleLabel.textAlignment = NSTextAlignmentCenter;
self.backButton = [UIButton buttonWithType:UIButtonTypeSystem];
[self.backButton setImage:[UIImage systemImageNamed:@"chevron.backward"] forState:UIControlStateNormal];
self.backButton.tintColor = UIColor.whiteColor;
[self.backButton addTarget:self action:@selector(onBack) forControlEvents:UIControlEventTouchUpInside];
for (UIView *sub in @[self.titleLabel, self.backButton]) {
sub.translatesAutoresizingMaskIntoConstraints = NO;
[self.headerBar addSubview:sub];
}
[NSLayoutConstraint activateConstraints:@[
[self.backButton.leadingAnchor constraintEqualToAnchor:self.headerBar.leadingAnchor constant:16],
[self.backButton.centerYAnchor constraintEqualToAnchor:self.headerBar.centerYAnchor],
[self.backButton.widthAnchor constraintEqualToConstant:24],
[self.backButton.heightAnchor constraintEqualToConstant:24],
[self.titleLabel.centerXAnchor constraintEqualToAnchor:self.headerBar.centerXAnchor],
[self.titleLabel.centerYAnchor constraintEqualToAnchor:self.headerBar.centerYAnchor],
]];
}
- (void)onBack {
[self dismissViewControllerAnimated:YES completion:nil];
}
@end
framework/QmsPluginFramework/QmsPluginFramework/Survey/SurveyViewController.h
View file @
02bbb1d6
// SurveyViewController.h
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface
SurveyViewController
:
UIViewController
@end
NS_ASSUME_NONNULL_END
framework/QmsPluginFramework/QmsPluginFramework/Survey/SurveyViewController.mm
View file @
02bbb1d6
// SurveyViewController.mm
#import "APIClient.h"
#import "SurveyViewController.h"
@interface SurveyViewController ()
// Header
@property (nonatomic, strong) UIView *headerBar;
@property (nonatomic, strong) UILabel *titleLabel;
@property (nonatomic, strong) UIButton *backButton;
@property (nonatomic, strong) UIScrollView *scrollView;
@property (nonatomic, strong) UIView *contentView;
@property (nonatomic, strong) UIButton *submitButton;
// Survey Components
@property (nonatomic, assign) NSInteger surveyId;
@property (nonatomic, copy) NSString *question;
@property (nonatomic, copy) NSString *type;
@property (nonatomic, copy) NSString *style;
@property (nonatomic, assign) NSInteger numberOfRates;
@property (nonatomic, assign) BOOL mandatory;
@property (nonatomic, strong) NSArray<NSDictionary *> *ratesChecklist;
@property (nonatomic, strong) NSMutableDictionary<NSNumber *, id> *answers;
@end
@implementation SurveyViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = UIColor.whiteColor;
self.answers = [NSMutableDictionary dictionary];
[self setupHeader];
[self setupScrollView];
[self requestSurvey];
}
#pragma mark - Header
- (void)setupHeader {
UILayoutGuide *safe = self.view.safeAreaLayoutGuide;
self.headerBar = [[UIView alloc] init];
self.headerBar.backgroundColor = [UIColor colorWithRed:0.0 green:0.43 blue:0.55 alpha:1.0];
self.headerBar.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:self.headerBar];
[NSLayoutConstraint activateConstraints:@[
[self.headerBar.topAnchor constraintEqualToAnchor:safe.topAnchor],
[self.headerBar.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
[self.headerBar.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
[self.headerBar.heightAnchor constraintEqualToConstant:44],
]];
self.titleLabel = [[UILabel alloc] init];
self.titleLabel.text = @"Handover Survey";
self.titleLabel.font = [UIFont systemFontOfSize:18 weight:UIFontWeightMedium];
self.titleLabel.textColor = UIColor.whiteColor;
self.titleLabel.textAlignment = NSTextAlignmentCenter;
self.backButton = [UIButton buttonWithType:UIButtonTypeSystem];
[self.backButton setImage:[UIImage systemImageNamed:@"chevron.backward"] forState:UIControlStateNormal];
self.backButton.tintColor = UIColor.whiteColor;
[self.backButton addTarget:self action:@selector(onBack) forControlEvents:UIControlEventTouchUpInside];
for (UIView *sub in @[self.titleLabel, self.backButton]) {
sub.translatesAutoresizingMaskIntoConstraints = NO;
[self.headerBar addSubview:sub];
}
[NSLayoutConstraint activateConstraints:@[
[self.backButton.leadingAnchor constraintEqualToAnchor:self.headerBar.leadingAnchor constant:16],
[self.backButton.centerYAnchor constraintEqualToAnchor:self.headerBar.centerYAnchor],
[self.backButton.widthAnchor constraintEqualToConstant:24],
[self.backButton.heightAnchor constraintEqualToConstant:24],
[self.titleLabel.centerXAnchor constraintEqualToAnchor:self.headerBar.centerXAnchor],
[self.titleLabel.centerYAnchor constraintEqualToAnchor:self.headerBar.centerYAnchor],
]];
}
- (void)onBack {
[self dismissViewControllerAnimated:YES completion:nil];
}
#pragma mark - Scroll View
- (void)setupScrollView {
UILayoutGuide *safe = self.view.safeAreaLayoutGuide;
self.scrollView = [[UIScrollView alloc] init];
self.scrollView.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:self.scrollView];
[NSLayoutConstraint activateConstraints:@[
[self.scrollView.topAnchor constraintEqualToAnchor:self.headerBar.bottomAnchor],
[self.scrollView.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
[self.scrollView.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor],
[self.scrollView.bottomAnchor constraintEqualToAnchor:safe.bottomAnchor],
]];
self.contentView = [[UIView alloc] init];
self.contentView.translatesAutoresizingMaskIntoConstraints = NO;
[self.scrollView addSubview:self.contentView];
[NSLayoutConstraint activateConstraints:@[
[self.contentView.topAnchor constraintEqualToAnchor:self.scrollView.topAnchor],
[self.contentView.leadingAnchor constraintEqualToAnchor:self.scrollView.leadingAnchor],
[self.contentView.trailingAnchor constraintEqualToAnchor:self.scrollView.trailingAnchor],
[self.contentView.bottomAnchor constraintEqualToAnchor:self.scrollView.bottomAnchor],
[self.contentView.widthAnchor constraintEqualToAnchor:self.scrollView.widthAnchor],
]];
}
- (void)renderSurvey:(NSDictionary *)surveyData {
NSArray *sections = surveyData[@"section"];
UIView *previous = nil;
for (NSDictionary *sectionDict in sections) {
NSString *sectionName = sectionDict[@"name"];
// Section Label
UIView *sectionHeader = [[UIView alloc] init];
sectionHeader.backgroundColor = UIColor.blackColor;
sectionHeader.translatesAutoresizingMaskIntoConstraints = NO;
UILabel *sectionLabel = [[UILabel alloc] init];
sectionLabel.text = sectionName;
sectionLabel.textColor = UIColor.whiteColor;
sectionLabel.font = [UIFont systemFontOfSize:16];
sectionLabel.numberOfLines = 0;
sectionLabel.translatesAutoresizingMaskIntoConstraints = NO;
[sectionHeader addSubview:sectionLabel];
[self.contentView addSubview:sectionHeader];
[NSLayoutConstraint activateConstraints:@[
[sectionHeader.leadingAnchor constraintEqualToAnchor:self.contentView.leadingAnchor constant:16],
[sectionHeader.trailingAnchor constraintEqualToAnchor:self.contentView.trailingAnchor constant:-16],
previous ?
[sectionHeader.topAnchor constraintEqualToAnchor:previous.bottomAnchor constant:24] :
[sectionHeader.topAnchor constraintEqualToAnchor:self.contentView.topAnchor constant:24],
[sectionLabel.topAnchor constraintEqualToAnchor:sectionHeader.topAnchor constant:12],
[sectionLabel.bottomAnchor constraintEqualToAnchor:sectionHeader.bottomAnchor constant:-12],
[sectionLabel.leadingAnchor constraintEqualToAnchor:sectionHeader.leadingAnchor constant:12],
[sectionLabel.trailingAnchor constraintEqualToAnchor:sectionHeader.trailingAnchor constant:-12],
]];
previous = sectionLabel;
// Questions
NSArray *surveys = sectionDict[@"survey"];
for (NSDictionary *surveyItem in surveys) {
UIView *questionView = [self buildQuestionView:surveyItem];
[self.contentView addSubview:questionView];
[NSLayoutConstraint activateConstraints:@[
[questionView.topAnchor constraintEqualToAnchor:previous.bottomAnchor constant:16],
[questionView.leadingAnchor constraintEqualToAnchor:self.contentView.leadingAnchor constant:16],
[questionView.trailingAnchor constraintEqualToAnchor:self.contentView.trailingAnchor constant:-16],
]];
previous = questionView;
}
}
self.submitButton = [UIButton buttonWithType:UIButtonTypeSystem];
[self.submitButton setTitle:@"Submit" forState:UIControlStateNormal];
self.submitButton.backgroundColor = [UIColor colorWithRed:0.0 green:0.43 blue:0.55 alpha:1.0];
[self.submitButton setTitleColor:UIColor.whiteColor forState:UIControlStateNormal];
self.submitButton.layer.cornerRadius = 8;
self.submitButton.translatesAutoresizingMaskIntoConstraints = NO;
[self.submitButton addTarget:self action:@selector(onSubmit) forControlEvents:UIControlEventTouchUpInside];
[self.contentView addSubview:self.submitButton];
[NSLayoutConstraint activateConstraints:@[
[self.submitButton.topAnchor constraintEqualToAnchor:previous.bottomAnchor constant:32],
[self.submitButton.leadingAnchor constraintEqualToAnchor:self.contentView.leadingAnchor constant:16],
[self.submitButton.trailingAnchor constraintEqualToAnchor:self.contentView.trailingAnchor constant:-16],
[self.submitButton.heightAnchor constraintEqualToConstant:48],
[self.submitButton.bottomAnchor constraintEqualToAnchor:self.contentView.bottomAnchor constant:-32],
]];
}
- (UIView *)buildQuestionView:(NSDictionary *)item {
NSString *type = item[@"type"];
NSString *style = item[@"style"];
if ([type isEqualToString:@"rate"]) {
if ([style isEqualToString:@"number"]) {
return [self buildNumberRating:item];
} else if ([style isEqualToString:@"star"]) {
return [self buildStarRating:item];
}
} else if ([type isEqualToString:@"comment"]) {
return [self buildCommentField:item];
} else if ([type isEqualToString:@"rateNps"]) {
return [self buildNPSRating:item];
} else {
return [self buildQuestionPlaceholder:item];
}
return [self buildQuestionPlaceholder:item];
}
- (UIView *)buildStarRating:(NSDictionary *)item {
UIView *container = [[UIView alloc] init];
container.translatesAutoresizingMaskIntoConstraints = NO;
container.tag = [item[@"id"] integerValue];
// Question
UILabel *label = [[UILabel alloc] init];
label.text = item[@"question"];
label.font = [UIFont systemFontOfSize:14 weight:UIFontWeightBold];
label.numberOfLines = 0;
label.translatesAutoresizingMaskIntoConstraints = NO;
[container addSubview:label];
// Helper
UILabel *helper = [[UILabel alloc] init];
helper.font = [UIFont systemFontOfSize:12];
helper.textColor = UIColor.grayColor;
helper.numberOfLines = 0;
helper.translatesAutoresizingMaskIntoConstraints = NO;
helper.text = [self buildRatesHelperText:item[@"rates_checklist_label"]];
helper.hidden = (helper.text.length == 0);
[container addSubview:helper];
// Stars container
UIView *starsContainer = [[UIView alloc] init];
starsContainer.translatesAutoresizingMaskIntoConstraints = NO;
[container addSubview:starsContainer];
NSInteger rawCount = [item[@"number_of_rates"] integerValue];
NSInteger starCount = MIN(rawCount, 5);
CGFloat size = 32;
CGFloat spacing = 8;
for (int i = 1; i <= starCount; i++) {
UIButton *star = [UIButton buttonWithType:UIButtonTypeCustom];
star.frame = CGRectMake((i - 1) * (size + spacing), 0, size, size);
star.tag = i;
[star setImage:[UIImage imageNamed:@"star_empty"] forState:UIControlStateNormal];
[star addTarget:self
action:@selector(onStarTapped:)
forControlEvents:UIControlEventTouchUpInside];
[starsContainer addSubview:star];
}
CGFloat totalWidth = starCount * size + (starCount - 1) * spacing;
[starsContainer.widthAnchor constraintEqualToConstant:totalWidth].active = YES;
[starsContainer.heightAnchor constraintEqualToConstant:size].active = YES;
// Constraints
[NSLayoutConstraint activateConstraints:@[
[label.topAnchor constraintEqualToAnchor:container.topAnchor],
[label.leadingAnchor constraintEqualToAnchor:container.leadingAnchor],
[label.trailingAnchor constraintEqualToAnchor:container.trailingAnchor],
helper.hidden ?
[starsContainer.topAnchor constraintEqualToAnchor:label.bottomAnchor constant:8] :
[helper.topAnchor constraintEqualToAnchor:label.bottomAnchor constant:6],
[helper.leadingAnchor constraintEqualToAnchor:container.leadingAnchor],
[helper.trailingAnchor constraintEqualToAnchor:container.trailingAnchor],
[starsContainer.topAnchor constraintEqualToAnchor:helper.bottomAnchor constant:8],
[starsContainer.leadingAnchor constraintEqualToAnchor:container.leadingAnchor],
[starsContainer.bottomAnchor constraintEqualToAnchor:container.bottomAnchor],
]];
return container;
}
- (void)onStarTapped:(UIButton *)sender {
UIView *starsContainer = sender.superview;
UIView *questionContainer = starsContainer.superview;
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
NSInteger selected = sender.tag;
for (UIButton *star in starsContainer.subviews) {
if (star.tag <= selected) {
[star setImage:[UIImage imageNamed:@"star_full" inBundle:bundle compatibleWithTraitCollection:nil]
forState:UIControlStateNormal];
} else {
[star setImage:[UIImage imageNamed:@"star_empty" inBundle:bundle compatibleWithTraitCollection:nil]
forState:UIControlStateNormal];
}
}
NSNumber *questionId = @(questionContainer.tag);
self.answers[questionId] = @(selected);
NSLog(@"⭐ Saved Q%@ = %@", questionId, self.answers[questionId]);
}
- (UIView *)buildCommentField:(NSDictionary *)item {
UIView *container = [[UIView alloc] init];
container.translatesAutoresizingMaskIntoConstraints = NO;
UILabel *label = [[UILabel alloc] init];
label.text = item[@"question"];
label.font = [UIFont systemFontOfSize:14 weight:UIFontWeightBold];
label.translatesAutoresizingMaskIntoConstraints = NO;
[container addSubview:label];
UITextField *textField = [[UITextField alloc] init];
textField.borderStyle = UITextBorderStyleRoundedRect;
textField.translatesAutoresizingMaskIntoConstraints = NO;
[container addSubview:textField];
textField.tag = [item[@"id"] integerValue];
[textField addTarget:self
action:@selector(onCommentChanged:)
forControlEvents:UIControlEventEditingChanged];
[NSLayoutConstraint activateConstraints:@[
[label.topAnchor constraintEqualToAnchor:container.topAnchor],
[label.leadingAnchor constraintEqualToAnchor:container.leadingAnchor],
[label.trailingAnchor constraintEqualToAnchor:container.trailingAnchor],
[textField.topAnchor constraintEqualToAnchor:label.bottomAnchor constant:8],
[textField.leadingAnchor constraintEqualToAnchor:container.leadingAnchor],
[textField.trailingAnchor constraintEqualToAnchor:container.trailingAnchor],
[textField.heightAnchor constraintEqualToConstant:32],
[textField.bottomAnchor constraintEqualToAnchor:container.bottomAnchor],
]];
container.tag = [item[@"id"] integerValue];
return container;
}
- (void)onCommentChanged:(UITextField *)field {
self.answers[@(field.tag)] = field.text ?: @"";
}
- (UIView *)buildNPSRating:(NSDictionary *)item {
UIView *container = [[UIView alloc] init];
container.translatesAutoresizingMaskIntoConstraints = NO;
container.tag = [item[@"id"] integerValue];
// Question
UILabel *label = [[UILabel alloc] init];
label.text = item[@"question"];
label.font = [UIFont systemFontOfSize:14 weight:UIFontWeightBold];
label.numberOfLines = 0;
label.translatesAutoresizingMaskIntoConstraints = NO;
[container addSubview:label];
// Helper
UILabel *helper = [[UILabel alloc] init];
helper.font = [UIFont systemFontOfSize:12];
helper.textColor = UIColor.grayColor;
helper.numberOfLines = 0;
helper.translatesAutoresizingMaskIntoConstraints = NO;
helper.text = [self buildRatesHelperText:item[@"rates_checklist_label"]];
helper.hidden = (helper.text.length == 0);
[container addSubview:helper];
// Buttons container
UIView *buttonsContainer = [[UIView alloc] init];
buttonsContainer.translatesAutoresizingMaskIntoConstraints = NO;
[container addSubview:buttonsContainer];
CGFloat size = 36;
CGFloat spacing = 8;
NSInteger value = 0;
CGFloat x = 0;
CGFloat y = 0;
for (int i = 0; i <= 10; i++) {
// Wrap after 8 → row 2 (9,10)
if (i == 9) {
x = 0;
y += size + spacing;
}
UIButton *btn = [UIButton buttonWithType:UIButtonTypeSystem];
btn.frame = CGRectMake(x, y, size, size);
btn.tag = i;
btn.layer.cornerRadius = 6;
btn.backgroundColor = [self npsColorForValue:i selected:NO];
[btn setTitle:[NSString stringWithFormat:@"%d", i] forState:UIControlStateNormal];
[btn setTitleColor:UIColor.blackColor forState:UIControlStateNormal];
[btn addTarget:self
action:@selector(onNPSTapped:)
forControlEvents:UIControlEventTouchUpInside];
[buttonsContainer addSubview:btn];
x += size + spacing;
}
CGFloat totalHeight = y + size;
[buttonsContainer.heightAnchor constraintEqualToConstant:totalHeight].active = YES;
// Constraints
[NSLayoutConstraint activateConstraints:@[
[label.topAnchor constraintEqualToAnchor:container.topAnchor],
[label.leadingAnchor constraintEqualToAnchor:container.leadingAnchor],
[label.trailingAnchor constraintEqualToAnchor:container.trailingAnchor],
helper.hidden ?
[buttonsContainer.topAnchor constraintEqualToAnchor:label.bottomAnchor constant:8] :
[helper.topAnchor constraintEqualToAnchor:label.bottomAnchor constant:6],
[helper.leadingAnchor constraintEqualToAnchor:container.leadingAnchor],
[helper.trailingAnchor constraintEqualToAnchor:container.trailingAnchor],
[buttonsContainer.topAnchor constraintEqualToAnchor:helper.bottomAnchor constant:8],
[buttonsContainer.leadingAnchor constraintEqualToAnchor:container.leadingAnchor],
[buttonsContainer.trailingAnchor constraintEqualToAnchor:container.trailingAnchor],
[buttonsContainer.bottomAnchor constraintEqualToAnchor:container.bottomAnchor],
]];
return container;
}
- (void)onNPSTapped:(UIButton *)sender {
UIView *buttonsContainer = sender.superview;
UIView *questionContainer = buttonsContainer.superview;
NSInteger selected = sender.tag;
for (UIButton *btn in buttonsContainer.subviews) {
BOOL isSelected = (btn.tag == selected);
btn.backgroundColor = [self npsColorForValue:btn.tag selected:isSelected];
[btn setTitleColor:isSelected ? UIColor.whiteColor : UIColor.blackColor
forState:UIControlStateNormal];
}
NSNumber *questionId = @(questionContainer.tag);
self.answers[questionId] = @(selected);
NSLog(@"📊 Saved NPS Q%@ = %@", questionId, self.answers[questionId]);
}
- (UIColor *)npsColorForValue:(NSInteger)value selected:(BOOL)selected {
UIColor *base;
if (value <= 1) {
base = [UIColor colorWithRed:0.86 green:0.23 blue:0.23 alpha:1.0]; // red
} else if (value <= 6) {
base = [UIColor colorWithRed:0.96 green:0.58 blue:0.11 alpha:1.0]; // orange
} else if (value <= 8) {
base = [UIColor colorWithRed:0.96 green:0.79 blue:0.11 alpha:1.0]; // yellow
} else {
base = [UIColor colorWithRed:0.24 green:0.70 blue:0.44 alpha:1.0]; // green
}
return selected ? base : [base colorWithAlphaComponent:0.35];
}
- (UIView *)buildNumberRating:(NSDictionary *)item {
UIView *container = [[UIView alloc] init];
container.translatesAutoresizingMaskIntoConstraints = NO;
container.tag = [item[@"id"] integerValue];
// Question
UILabel *label = [[UILabel alloc] init];
label.text = item[@"question"];
label.font = [UIFont systemFontOfSize:14 weight:UIFontWeightBold];
label.numberOfLines = 0;
label.translatesAutoresizingMaskIntoConstraints = NO;
[container addSubview:label];
// Helper
UILabel *helper = [[UILabel alloc] init];
helper.font = [UIFont systemFontOfSize:12];
helper.textColor = UIColor.grayColor;
helper.numberOfLines = 0;
helper.translatesAutoresizingMaskIntoConstraints = NO;
helper.text = [self buildRatesHelperText:item[@"rates_checklist_label"]];
helper.hidden = (helper.text.length == 0);
[container addSubview:helper];
// Buttons container (wrap layout)
UIView *buttonsContainer = [[UIView alloc] init];
buttonsContainer.translatesAutoresizingMaskIntoConstraints = NO;
[container addSubview:buttonsContainer];
NSInteger count = [item[@"number_of_rates"] integerValue];
CGFloat buttonSize = 40;
CGFloat spacing = 8;
CGFloat maxWidth = UIScreen.mainScreen.bounds.size.width - 32 - 32;
CGFloat x = 0;
CGFloat y = 0;
for (int i = 1; i <= count; i++) {
if (x + buttonSize > maxWidth) {
x = 0;
y += buttonSize + spacing;
}
UIButton *btn = [UIButton buttonWithType:UIButtonTypeSystem];
btn.frame = CGRectMake(x, y, buttonSize, buttonSize);
btn.tag = i;
btn.layer.cornerRadius = 6;
btn.layer.borderWidth = 1;
btn.layer.borderColor = UIColor.lightGrayColor.CGColor;
btn.backgroundColor = UIColor.whiteColor;
[btn setTitle:[NSString stringWithFormat:@"%d", i] forState:UIControlStateNormal];
[btn setTitleColor:UIColor.blackColor forState:UIControlStateNormal];
[btn addTarget:self
action:@selector(onNumberTapped:)
forControlEvents:UIControlEventTouchUpInside];
[buttonsContainer addSubview:btn];
x += buttonSize + spacing;
}
CGFloat totalHeight = y + buttonSize;
[buttonsContainer.heightAnchor constraintEqualToConstant:totalHeight].active = YES;
// Constraints
[NSLayoutConstraint activateConstraints:@[
[label.topAnchor constraintEqualToAnchor:container.topAnchor],
[label.leadingAnchor constraintEqualToAnchor:container.leadingAnchor],
[label.trailingAnchor constraintEqualToAnchor:container.trailingAnchor],
helper.hidden ?
[buttonsContainer.topAnchor constraintEqualToAnchor:label.bottomAnchor constant:8] :
[helper.topAnchor constraintEqualToAnchor:label.bottomAnchor constant:6],
[helper.leadingAnchor constraintEqualToAnchor:container.leadingAnchor],
[helper.trailingAnchor constraintEqualToAnchor:container.trailingAnchor],
[buttonsContainer.topAnchor constraintEqualToAnchor:helper.bottomAnchor constant:8],
[buttonsContainer.leadingAnchor constraintEqualToAnchor:container.leadingAnchor],
[buttonsContainer.trailingAnchor constraintEqualToAnchor:container.trailingAnchor],
[buttonsContainer.bottomAnchor constraintEqualToAnchor:container.bottomAnchor],
]];
return container;
}
- (void)onNumberTapped:(UIButton *)sender {
UIView *buttonsContainer = sender.superview;
UIView *questionContainer = buttonsContainer.superview;
// Reset all buttons
for (UIButton *btn in buttonsContainer.subviews) {
btn.backgroundColor = UIColor.whiteColor;
btn.layer.borderColor = UIColor.lightGrayColor.CGColor;
[btn setTitleColor:UIColor.blackColor forState:UIControlStateNormal];
}
// Highlight selected
sender.backgroundColor = [UIColor colorWithRed:0.0 green:0.43 blue:0.55 alpha:1.0];
sender.layer.borderColor = UIColor.clearColor.CGColor;
[sender setTitleColor:UIColor.whiteColor forState:UIControlStateNormal];
NSNumber *questionId = @(questionContainer.tag);
self.answers[questionId] = @(sender.tag);
NSLog(@"🔢 Saved Q%@ = %@", questionId, self.answers[questionId]);
}
- (UIView *)buildQuestionPlaceholder:(NSDictionary *)item {
UIView *container = [[UIView alloc] init];
container.translatesAutoresizingMaskIntoConstraints = NO;
UILabel *label = [[UILabel alloc] init];
label.text = item[@"question"];
label.font = [UIFont systemFontOfSize:14 weight:UIFontWeightBold];
label.translatesAutoresizingMaskIntoConstraints = NO;
[container addSubview:label];
UISegmentedControl *seg = [[UISegmentedControl alloc] initWithItems:@[@"0",@"1",@"2",@"3",@"4",@"5",@"6",@"7",@"8",@"9",@"10"]];
seg.translatesAutoresizingMaskIntoConstraints = NO;
[container addSubview:seg];
[NSLayoutConstraint activateConstraints:@[
[label.topAnchor constraintEqualToAnchor:container.topAnchor],
[label.leadingAnchor constraintEqualToAnchor:container.leadingAnchor],
[label.trailingAnchor constraintEqualToAnchor:container.trailingAnchor],
[seg.topAnchor constraintEqualToAnchor:label.bottomAnchor constant:8],
[seg.leadingAnchor constraintEqualToAnchor:container.leadingAnchor],
[seg.trailingAnchor constraintEqualToAnchor:container.trailingAnchor],
[seg.bottomAnchor constraintEqualToAnchor:container.bottomAnchor],
]];
return container;
}
- (void)onSubmit {
NSLog(@"submit pressed");
// Validate mandatory fields
// for (NSDictionary *section in self.surveyData[@"section"]) {
// for (NSDictionary *q in section[@"survey"]) {
// BOOL mandatory = [q[@"mandatory"] boolValue];
// NSInteger qId = [q[@"id"] integerValue];
// if (mandatory && (!self.answers[@(qId)] ||
// [self.answers[@(qId)] isEqual:@""])) {
// NSLog(@"❌ Question %ld is mandatory", (long)qId);
// return;
// }
// }
// }
//
// // Call API
// [self requestSubmitSurvey];
}
#pragma mark - API
-(void)requestSurvey {
[APIClient requestSurvey:^(BOOL success, NSDictionary * _Nullable data, NSError * _Nullable error) {
// Save debug file
NSString *plainText = [NSString stringWithFormat:@"%@", data];
NSString *docsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
NSString *filePath = [docsPath stringByAppendingPathComponent:@"survey.txt"];
[plainText writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:nil];
NSLog(@"📁 survey saved as plain text to: %@", filePath);
if (error || !data) {
NSLog(@"❌ survey API failed: %@", error);
return;
}
if (data[@"status_code"]) {
NSLog(@"❌ survey status error: %@", data[@"message"]);
return;
}
NSDictionary *appData = data[@"AppData"];
if (![appData[@"status"] isEqualToString:@"success"]) {
NSLog(@"❌ survey failure: %@", appData[@"message"]);
return;
}
NSArray *surveyArray = data[@"Data"];
if (![surveyArray isKindOfClass:[NSArray class]] || surveyArray.count == 0) {
NSLog(@"❌ Invalid notification format");
return;
}
NSDictionary *survey = surveyArray[0]; // First survey object
if (![survey isKindOfClass:[NSDictionary class]]) {
NSLog(@"❌ Invalid survey format");
return;
}
dispatch_async(dispatch_get_main_queue(), ^{
self.surveyId = [survey[@"id"] integerValue];
self.question = survey[@"question"];
self.type = survey[@"type"];
self.style = survey[@"style"];
self.numberOfRates = [survey[@"number_of_rates"] integerValue];
self.mandatory = [survey[@"mandatory"] boolValue];
self.ratesChecklist = survey[@"rates_checklist_label"];
[self renderSurvey:survey];
});
}];
}
- (void)requestSubmitSurvey {
// NSMutableArray *payload = [NSMutableArray array];
// [self.answers enumerateKeysAndObjectsUsingBlock:^(NSNumber *surveyId, id answer, BOOL *stop) {
// [payload addObject:@{@"surveyId": surveyId, @"value": answer}];
// }];
//
// [APIClient submitSurvey:payload completion:^(BOOL success, NSError *error) {
// if (success) {
// NSLog(@"✅ Survey submitted!");
// [self dismissViewControllerAnimated:YES completion:nil];
// } else {
// NSLog(@"❌ Failed to submit survey: %@", error);
// }
// }];
}
- (NSString *)buildRatesHelperText:(NSArray *)labels {
if (![labels isKindOfClass:[NSArray class]] || labels.count == 0) {
return nil;
}
NSMutableArray *parts = [NSMutableArray array];
for (NSDictionary *item in labels) {
NSNumber *value = item[@"value"];
NSString *label = item[@"label"];
if (value && label && ![label isKindOfClass:[NSNull class]]) {
[parts addObject:[NSString stringWithFormat:@"%@ = %@", value, label]];
}
}
return parts.count > 0 ? [parts componentsJoinedByString:@" "] : nil;
}
@end
framework/QmsPluginFramework/QmsPluginFramework/Utilities/API/APIClient.h
View file @
02bbb1d6
...
...
@@ -106,6 +106,8 @@ NS_ASSUME_NONNULL_BEGIN
NSDictionary
*
_Nullable
response
,
NSError
*
_Nullable
error
))
completion
;
+
(
void
)
requestSurvey
:(
void
(
^
)(
BOOL
success
,
NSDictionary
*
_Nullable
response
,
NSError
*
_Nullable
error
))
completion
;
@end
NS_ASSUME_NONNULL_END
framework/QmsPluginFramework/QmsPluginFramework/Utilities/API/APIClient.mm
View file @
02bbb1d6
...
...
@@ -1585,9 +1585,10 @@
+ (void)requestDefectMatrix:(void(^)(BOOL success, NSDictionary * _Nullable response, NSError * _Nullable error))completion {
// if ([APIConfig handleOfflineForAPI:@"DefectMatrix" completion:completion]) {
// return;
// }
// if ([APIConfig handleOfflineForAPI:@"DefectMatrix" completion:completion]) {
// return;
// }
// ✅ URL
NSURL *url = [APIConfig urlWithPath:@"/framework/issue/getGeneralSettingByUnit"];
...
...
@@ -2553,6 +2554,76 @@
[task resume];
}
+ (void)requestSurvey:(void(^)(BOOL success, NSDictionary * _Nullable response, NSError * _Nullable error))completion {
if ([APIConfig handleOfflineForAPI:@"Survey" completion:completion]) {
return;
}
NSURL *url = [APIConfig urlWithPath:@"/framework/owner/survey/getSurvey"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST";
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
[request setValue:@"application/json" forHTTPHeaderField:@"Accept"];
NSDictionary *jsonBody = @{
@"data": @{
@"os": [APIConfig os],
@"drawing_plan_id": [APIConfig drawingPlanId] ?: @"",
}
};
NSLog(@"🌍 request survey: %@", url.absoluteString);
NSLog(@"📦 request survey json: %@", jsonBody);
NSError *jsonError;
NSData *bodyData = [NSJSONSerialization dataWithJSONObject:jsonBody options:0 error:&jsonError];
if (jsonError) {
NSLog(@"❌ JSON Serialization Error: %@", jsonError);
if (completion) completion(NO, nil, jsonError);
return;
}
request.HTTPBody = bodyData;
NSURLSessionDataTask *task = [[NSURLSession sharedSession]
dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (error) {
NSLog(@"❌ Network Error: %@", error);
dispatch_async(dispatch_get_main_queue(), ^{
completion(NO, nil, error);
});
return;
}
NSError *parseError;
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];
if (parseError) {
NSLog(@"⚠️ JSON Parse Error: %@", parseError);
dispatch_async(dispatch_get_main_queue(), ^{
completion(NO, nil, parseError);
});
return;
}
NSString *cacheKey = [NSString stringWithFormat:@"Survey_%@", [APIConfig projectId]];
NSDictionary *safeJson = [APIConfig dictionaryByReplacingNullsWithBlanks:json];
[LocalStorage saveDictionary:safeJson forKey:cacheKey];
NSLog(@"💾 Saved to cache: %@", cacheKey);
dispatch_async(dispatch_get_main_queue(), ^{
completion(YES, json, nil);
});
}];
[task resume];
}
#pragma mark - Helper Methods
+ (NSString *)multipartFormField:(NSString *)name value:(NSString *)value boundary:(NSString *)boundary {
return [NSString stringWithFormat:@"--%@\r\nContent-Disposition: form-data; name=\"data[%@]\"\r\n\r\n%@\r\n", boundary, name, value];
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment