@convep_mobilogy/react-native-qms-plugin 1.8.13 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/QmsPlugin.podspec +2 -4
  2. package/QmsPluginFramework.podspec +12 -0
  3. package/README.md +183 -4
  4. package/android/build.gradle +2 -1
  5. package/android/consumer-rules.pro +13 -0
  6. package/ios/QmsPlugin/QmsDashboardViewManager.mm +68 -26
  7. package/ios/QmsPlugin/QmsModule.mm +1293 -0
  8. package/ios/QmsPlugin/UIViewController+QmsAnalytics.m +3 -1
  9. package/lib/module/QmsDashboardView.android.js +121 -9
  10. package/lib/module/QmsDashboardView.android.js.map +1 -1
  11. package/lib/module/QmsPlugin.js +57 -0
  12. package/lib/module/QmsPlugin.js.map +1 -1
  13. package/lib/typescript/src/QmsDashboardView.android.d.ts.map +1 -1
  14. package/lib/typescript/src/QmsPlugin.d.ts +137 -0
  15. package/lib/typescript/src/QmsPlugin.d.ts.map +1 -1
  16. package/lib/typescript/src/index.d.ts +1 -1
  17. package/lib/typescript/src/index.d.ts.map +1 -1
  18. package/package.json +7 -4
  19. package/src/QmsDashboardView.android.tsx +156 -11
  20. package/src/QmsPlugin.ts +312 -0
  21. package/src/index.tsx +13 -1
  22. package/ios/QmsPluginFramework.xcframework/Info.plist +0 -44
  23. package/ios/QmsPluginFramework.xcframework/ios-arm64/QmsPluginFramework.framework/Assets.car +0 -0
  24. package/ios/QmsPluginFramework.xcframework/ios-arm64/QmsPluginFramework.framework/Headers/QmsPlugin.h +0 -43
  25. package/ios/QmsPluginFramework.xcframework/ios-arm64/QmsPluginFramework.framework/Headers/QmsPluginFramework.h +0 -17
  26. package/ios/QmsPluginFramework.xcframework/ios-arm64/QmsPluginFramework.framework/Info.plist +0 -0
  27. package/ios/QmsPluginFramework.xcframework/ios-arm64/QmsPluginFramework.framework/Modules/module.modulemap +0 -6
  28. package/ios/QmsPluginFramework.xcframework/ios-arm64/QmsPluginFramework.framework/Montserrat-Bold.ttf +0 -0
  29. package/ios/QmsPluginFramework.xcframework/ios-arm64/QmsPluginFramework.framework/Montserrat-BoldItalic.ttf +0 -0
  30. package/ios/QmsPluginFramework.xcframework/ios-arm64/QmsPluginFramework.framework/Montserrat-ExtraBold.ttf +0 -0
  31. package/ios/QmsPluginFramework.xcframework/ios-arm64/QmsPluginFramework.framework/Montserrat-ExtraLight.ttf +0 -0
  32. package/ios/QmsPluginFramework.xcframework/ios-arm64/QmsPluginFramework.framework/Montserrat-Italic.ttf +0 -0
  33. package/ios/QmsPluginFramework.xcframework/ios-arm64/QmsPluginFramework.framework/Montserrat-Light.ttf +0 -0
  34. package/ios/QmsPluginFramework.xcframework/ios-arm64/QmsPluginFramework.framework/Montserrat-Medium.ttf +0 -0
  35. package/ios/QmsPluginFramework.xcframework/ios-arm64/QmsPluginFramework.framework/Montserrat-Regular.ttf +0 -0
  36. package/ios/QmsPluginFramework.xcframework/ios-arm64/QmsPluginFramework.framework/Montserrat-SemiBold.ttf +0 -0
  37. package/ios/QmsPluginFramework.xcframework/ios-arm64/QmsPluginFramework.framework/Montserrat-Thin.ttf +0 -0
  38. package/ios/QmsPluginFramework.xcframework/ios-arm64/QmsPluginFramework.framework/QmsPluginFramework +0 -0
  39. package/ios/QmsPluginFramework.xcframework/ios-arm64/QmsPluginFramework.framework/_CodeSignature/CodeResources +0 -289
  40. package/ios/QmsPluginFramework.xcframework/ios-arm64/QmsPluginFramework.framework/bm.json +0 -781
  41. package/ios/QmsPluginFramework.xcframework/ios-arm64/QmsPluginFramework.framework/cn.json +0 -782
  42. package/ios/QmsPluginFramework.xcframework/ios-arm64/QmsPluginFramework.framework/en.json +0 -782
  43. package/ios/QmsPluginFramework.xcframework/ios-arm64_x86_64-simulator/QmsPluginFramework.framework/Assets.car +0 -0
  44. package/ios/QmsPluginFramework.xcframework/ios-arm64_x86_64-simulator/QmsPluginFramework.framework/Headers/QmsPlugin.h +0 -43
  45. package/ios/QmsPluginFramework.xcframework/ios-arm64_x86_64-simulator/QmsPluginFramework.framework/Headers/QmsPluginFramework.h +0 -17
  46. package/ios/QmsPluginFramework.xcframework/ios-arm64_x86_64-simulator/QmsPluginFramework.framework/Info.plist +0 -0
  47. package/ios/QmsPluginFramework.xcframework/ios-arm64_x86_64-simulator/QmsPluginFramework.framework/Modules/module.modulemap +0 -6
  48. package/ios/QmsPluginFramework.xcframework/ios-arm64_x86_64-simulator/QmsPluginFramework.framework/Montserrat-Bold.ttf +0 -0
  49. package/ios/QmsPluginFramework.xcframework/ios-arm64_x86_64-simulator/QmsPluginFramework.framework/Montserrat-BoldItalic.ttf +0 -0
  50. package/ios/QmsPluginFramework.xcframework/ios-arm64_x86_64-simulator/QmsPluginFramework.framework/Montserrat-ExtraBold.ttf +0 -0
  51. package/ios/QmsPluginFramework.xcframework/ios-arm64_x86_64-simulator/QmsPluginFramework.framework/Montserrat-ExtraLight.ttf +0 -0
  52. package/ios/QmsPluginFramework.xcframework/ios-arm64_x86_64-simulator/QmsPluginFramework.framework/Montserrat-Italic.ttf +0 -0
  53. package/ios/QmsPluginFramework.xcframework/ios-arm64_x86_64-simulator/QmsPluginFramework.framework/Montserrat-Light.ttf +0 -0
  54. package/ios/QmsPluginFramework.xcframework/ios-arm64_x86_64-simulator/QmsPluginFramework.framework/Montserrat-Medium.ttf +0 -0
  55. package/ios/QmsPluginFramework.xcframework/ios-arm64_x86_64-simulator/QmsPluginFramework.framework/Montserrat-Regular.ttf +0 -0
  56. package/ios/QmsPluginFramework.xcframework/ios-arm64_x86_64-simulator/QmsPluginFramework.framework/Montserrat-SemiBold.ttf +0 -0
  57. package/ios/QmsPluginFramework.xcframework/ios-arm64_x86_64-simulator/QmsPluginFramework.framework/Montserrat-Thin.ttf +0 -0
  58. package/ios/QmsPluginFramework.xcframework/ios-arm64_x86_64-simulator/QmsPluginFramework.framework/QmsPluginFramework +0 -0
  59. package/ios/QmsPluginFramework.xcframework/ios-arm64_x86_64-simulator/QmsPluginFramework.framework/_CodeSignature/CodeResources +0 -289
  60. package/ios/QmsPluginFramework.xcframework/ios-arm64_x86_64-simulator/QmsPluginFramework.framework/bm.json +0 -781
  61. package/ios/QmsPluginFramework.xcframework/ios-arm64_x86_64-simulator/QmsPluginFramework.framework/cn.json +0 -782
  62. package/ios/QmsPluginFramework.xcframework/ios-arm64_x86_64-simulator/QmsPluginFramework.framework/en.json +0 -782
package/QmsPlugin.podspec CHANGED
@@ -14,16 +14,14 @@ Pod::Spec.new do |s|
14
14
  s.homepage = homepage
15
15
  s.license = package["license"]
16
16
  s.authors = package["author"]
17
- s.platforms = { :ios => "12.4" }
17
+ s.platforms = { :ios => "15.1" }
18
18
 
19
19
  s.source = { :git => "http://convep.ddns.me:20000/WeiHan/weihan-plugin.git", :tag => "#{s.version}" }
20
20
  s.source_files = "ios/QmsPlugin/**/*.{h,m,mm,cpp}", "ios/QmsPluginSpec/**/*.{h,mm}"
21
21
  s.private_header_files = "ios/QmsPlugin/**/*.h"
22
22
  s.public_header_files = "ios/QmsPluginSpec/**/*.h"
23
23
 
24
- s.preserve_paths = "ios/QmsPluginFramework.xcframework"
25
- s.vendored_frameworks = "ios/QmsPluginFramework.xcframework"
26
-
24
+ s.dependency "QmsPluginFramework", "1.31.0"
27
25
  s.dependency "React-Core"
28
26
  s.dependency "ReactCommon"
29
27
  s.dependency "React-Codegen"
@@ -0,0 +1,12 @@
1
+ Pod::Spec.new do |s|
2
+ s.name = "QmsPluginFramework"
3
+ s.version = "1.31.0"
4
+ s.summary = "QMS Native iOS Framework"
5
+ s.description = "Native QMS iOS SDK distributed as XCFramework."
6
+ s.homepage = "https://sdk.convep.com"
7
+ s.license = { :type => "Proprietary", :text => "Copyright © Convep Mobilogy. All rights reserved." }
8
+ s.author = { "Convep Mobilogy" => "developer@convep.com" }
9
+ s.platform = :ios, "15.1"
10
+ s.source = { :http => "https://sdk.convep.com/ios/QMS/qms-plugin/1.31.0", :type => "zip", :sha256 => "6bae9da81e3b0df77a98edf939279290990539e954407dae90904b96192705cf" }
11
+ s.vendored_frameworks = "QmsPluginFramework.xcframework"
12
+ end
package/README.md CHANGED
@@ -2,6 +2,45 @@
2
2
 
3
3
  A React Native plugin that provides QMS (Quality Management System) components and APIs for defect management, dashboard visualization, and QMS workflow operations.
4
4
 
5
+ ## Development setup
6
+
7
+ The development toolchain is managed with [mise](https://mise.jdx.dev/). From
8
+ the repository root, install the pinned Node.js, Java, and Ruby versions and
9
+ the JavaScript dependencies:
10
+
11
+ ```sh
12
+ mise install
13
+ mise run setup
14
+ ```
15
+
16
+ The project pins Yarn through the `packageManager` field in `package.json`; the
17
+ setup task enables Corepack before running the immutable Yarn install. Android
18
+ SDK tooling and Xcode are platform dependencies and are not installed by mise.
19
+
20
+ See [Contributing](CONTRIBUTING.md) for the complete development workflow and
21
+ available mise tasks.
22
+
23
+ To create a separate framework workspace with npm and local-development example
24
+ apps, run:
25
+
26
+ ```sh
27
+ ./setup_framework.sh
28
+ ```
29
+
30
+ The script confirms before moving this repository into a new
31
+ `QmsPlugin_Framework` directory one level above its current location. See the
32
+ contributing guide for the generated layout and non-interactive usage. It adds
33
+ mise configuration to both generated apps and runs `mise install` plus
34
+ `mise run setup` in the plugin and each app.
35
+
36
+ For direct Kotlin/Java and Swift/Objective-C integration with the native AAR
37
+ and XCFramework, see
38
+ [Native QMS SDK Integration for Android and iOS](docs/ANDROID_IOS_INTEGRATION.md).
39
+
40
+ For native Android development, AAR/Maven generation, example-app testing, and
41
+ npm publishing, see
42
+ [Android SDK Development and npm Release](docs/ANDROID_DEVELOPMENT_AND_RELEASE.md).
43
+
5
44
  ---
6
45
 
7
46
  # Installation
@@ -22,20 +61,132 @@ yarn add @convep_mobilogy/react-native-qms-plugin
22
61
 
23
62
  # iOS Setup
24
63
 
25
- Autolinking should set up everything automatically.
64
+ Add the hosted native framework pod to the application `Podfile`. The podspec
65
+ is shipped with the npm package and CocoaPods downloads the matching ZIP from
66
+ `sdk.convep.com`:
67
+
68
+ ```ruby
69
+ qms_plugin_package = Pod::Executable.execute_command('node', ['-p',
70
+ 'require.resolve(
71
+ "@convep_mobilogy/react-native-qms-plugin/package.json",
72
+ {paths: [process.argv[1]]},
73
+ )', __dir__]).strip
74
+
75
+ target 'YourApp' do
76
+ pod 'QmsPluginFramework',
77
+ :path => File.dirname(qms_plugin_package)
78
+ end
79
+ ```
26
80
 
27
- 1. Navigate to your iOS folder:
81
+ Autolinking sets up the React Native wrapper. From the application's root
82
+ directory, install all CocoaPods dependencies:
28
83
 
29
84
  ```sh
30
85
  cd ios
31
86
  pod install
32
87
  ```
33
88
 
89
+ ## iOS framework development
90
+
91
+ Use the following workflow when changing the native iOS framework and testing
92
+ it in the included example app.
93
+
94
+ ### 1. Open the framework project
95
+
96
+ Open the following project in Xcode and make the required native changes:
97
+
98
+ ```text
99
+ framework/ios_framework/QmsPluginFramework.xcodeproj
100
+ ```
101
+
102
+ ### 2. Build and publish the XCFramework
103
+
104
+ From the plugin root, run the build helper. It archives the framework for both
105
+ iOS devices and the iOS Simulator, creates the XCFramework, packages it, and
106
+ uploads the versioned ZIP used by the native CocoaPod:
107
+
108
+ ```sh
109
+ ./compile_framework.sh ios
110
+ ```
111
+
112
+ You can optionally provide the SDK version:
113
+
114
+ ```sh
115
+ ./compile_framework.sh ios <sdk-version>
116
+ ```
117
+
118
+ The versioned XCFramework ZIP and its SHA-256 checksum are written to
119
+ `framework/ios_framework/build/releases/<sdk-version>/`. The root
120
+ `QmsPluginFramework.podspec` is updated for the release. The native pod has no
121
+ React dependency. The command uploads the ZIP to
122
+ `https://sdk.convep.com/api/uploadIOS` using the same credentials and upload
123
+ fields as Android. See [the iOS release guide](docs/IOS_COCOAPODS_RELEASE.md)
124
+ for upload settings and the client Podfile.
125
+
126
+ To build the XCFramework manually instead, run the following commands:
127
+
128
+ ```sh
129
+ cd framework/ios_framework
130
+ rm -rf QmsPluginFramework.xcframework
131
+
132
+ xcodebuild archive \
133
+ -project QmsPluginFramework.xcodeproj \
134
+ -scheme QmsPluginFramework \
135
+ -destination "generic/platform=iOS" \
136
+ -archivePath ./build/ios_devices.xcarchive \
137
+ SKIP_INSTALL=NO \
138
+ BUILD_LIBRARY_FOR_DISTRIBUTION=YES
139
+
140
+ xcodebuild archive \
141
+ -project QmsPluginFramework.xcodeproj \
142
+ -scheme QmsPluginFramework \
143
+ -destination "generic/platform=iOS Simulator" \
144
+ -archivePath ./build/ios_simulators.xcarchive \
145
+ SKIP_INSTALL=NO \
146
+ BUILD_LIBRARY_FOR_DISTRIBUTION=YES
147
+
148
+ xcodebuild -create-xcframework \
149
+ -framework ./build/ios_devices.xcarchive/Products/Library/Frameworks/QmsPluginFramework.framework \
150
+ -framework ./build/ios_simulators.xcarchive/Products/Library/Frameworks/QmsPluginFramework.framework \
151
+ -output ./QmsPluginFramework.xcframework
152
+ ```
153
+
154
+ ### 3. Install the example app's iOS dependencies
155
+
156
+ From the plugin root, run this after the uploaded version is available so
157
+ CocoaPods downloads the latest native build:
158
+
159
+ ```sh
160
+ cd example/ios
161
+ pod install
162
+ ```
163
+
164
+ ### 4. Start Metro
165
+
166
+ In a separate terminal, start Metro from the plugin root:
167
+
168
+ ```sh
169
+ cd example
170
+ npx react-native start
171
+ ```
172
+
173
+ ### 5. Build and run the example app
174
+
175
+ Open the CocoaPods workspace (not the `.xcodeproj`) and run the desired iOS
176
+ Simulator or connected device:
177
+
178
+ ```text
179
+ example/ios/QmsPluginExample.xcworkspace
180
+ ```
181
+
182
+ Repeat steps 2 through 5 after each native framework change.
183
+
34
184
  ---
35
185
 
36
186
  # Android Setup
37
187
 
38
- Autolinking should set up everything automatically, but you need to add the private Maven repo and enable core library desugaring.
188
+ Autolinking should set up everything automatically, but you need to add the QMS
189
+ Maven repository and enable core library desugaring.
39
190
 
40
191
  1. In `android/build.gradle`, add the Maven repo:
41
192
 
@@ -47,7 +198,10 @@ allprojects {
47
198
  maven { url = uri("https://www.jitpack.io") }
48
199
 
49
200
  maven {
50
- url = uri("https://raw.githubusercontent.com/ConvepMobilogy/QMS_framework/main/maven-repo")
201
+ url = uri("https://sdk.convep.com/maven")
202
+ content {
203
+ includeGroup("com.convep.qms")
204
+ }
51
205
  }
52
206
  }
53
207
  }
@@ -125,6 +279,31 @@ const styles = StyleSheet.create({
125
279
 
126
280
  ---
127
281
 
282
+ ## Open Add Issue Directly
283
+
284
+ Use `QmsPlugin.openAddIssue` when the host app already knows the target unit and wants to skip the QMS project/unit/dashboard flow.
285
+
286
+ ```tsx
287
+ import { QmsPlugin } from '@convep_mobilogy/react-native-qms-plugin';
288
+
289
+ async function handleAddIssuePress() {
290
+ await QmsPlugin.openAddIssue({
291
+ userToken: '<UserToken>',
292
+ clientID: '<ClientID>',
293
+ clientCode: '<CompanyCode>',
294
+ isOrigin: false,
295
+ drawingPlanId: 1183,
296
+ projectId: 4,
297
+ unitName: 'C-L(7)',
298
+ projectName: 'Ara E Residence Dev',
299
+ });
300
+ }
301
+ ```
302
+
303
+ `drawingPlanId` is required because Add Issue needs a selected unit plan. `projectId`, `unitName`, and `projectName` are optional but recommended so the SDK can display and cache the correct context.
304
+
305
+ ---
306
+
128
307
  # Props
129
308
 
130
309
  ## QmsDashboardView Props
@@ -43,6 +43,7 @@ android {
43
43
  defaultConfig {
44
44
  minSdkVersion getExtOrIntegerDefault("minSdkVersion")
45
45
  targetSdkVersion getExtOrIntegerDefault("targetSdkVersion")
46
+ consumerProguardFiles "consumer-rules.pro"
46
47
  }
47
48
 
48
49
  buildFeatures {
@@ -85,7 +86,7 @@ dependencies {
85
86
  implementation "com.facebook.react:react-android"
86
87
  implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
87
88
  implementation 'com.facebook.react:react-native:+'
88
- implementation "com.convep.qms:qms-plugin:1.26.2"
89
+ implementation "com.convep.qms:qms-plugin:1.31.0"
89
90
  implementation 'androidx.appcompat:appcompat:1.6.1'
90
91
 
91
92
  implementation 'com.squareup.okhttp3:logging-interceptor:4.12.0'
@@ -0,0 +1,13 @@
1
+ # React Native creates this package from the generated PackageList. Keep the
2
+ # package and its native module entry points stable in minified host apps.
3
+ -keep class com.qmsplugin.QmsPluginPackage { *; }
4
+ -keep class com.qmsplugin.QmsPluginModule { *; }
5
+
6
+ # The wrapper returns the native framework module to React Native. ReactMethod
7
+ # methods are invoked through bridge metadata rather than ordinary Java calls.
8
+ -keep class com.convep.qms.QmsModule { *; }
9
+ -keepclassmembers class com.convep.qms.** {
10
+ @com.facebook.react.bridge.ReactMethod <methods>;
11
+ }
12
+
13
+ -keepattributes RuntimeVisibleAnnotations,RuntimeVisibleParameterAnnotations,AnnotationDefault
@@ -310,7 +310,7 @@ static NSString * const kQmsRoutePayloadNotification = @"QmsRoutePayloadNotifica
310
310
  @"new_issue", @"new_info", @"accept_issue", @"reject_issue", @"decline_issue",
311
311
  @"start_fix", @"redo", @"completed", @"close_internal", @"close_external",
312
312
  @"pending_owner_acceptance", @"assign_issue", @"reassign_issue", @"poa_reminder",
313
- @"auto_close_reminder", @"pending_access_reminder", @"overdue_reminder",
313
+ @"auto_close_reminder", @"overdue_reminder",
314
314
  @"issue_summary_reminder", @"lodge_summary_reminder", @"overdue_summary_reminder"
315
315
  ]];
316
316
  });
@@ -322,8 +322,15 @@ static NSString * const kQmsRoutePayloadNotification = @"QmsRoutePayloadNotifica
322
322
  [type isEqualToString:@"unattend_appt_reminder"];
323
323
  }
324
324
 
325
- - (BOOL)isDraftReminderType:(NSString *)type {
326
- return [type isEqualToString:@"draft_reminder"];
325
+ - (BOOL)isDashboardType:(NSString *)type {
326
+ static NSSet<NSString *> *types;
327
+ static dispatch_once_t onceToken;
328
+ dispatch_once(&onceToken, ^{
329
+ types = [NSSet setWithArray:@[
330
+ @"pending_access_reminder", @"completion_letter", @"key_collection", @"defect_summary", @"clearance_letter"
331
+ ]];
332
+ });
333
+ return type.length > 0 && [types containsObject:type];
327
334
  }
328
335
 
329
336
  - (void)applyPayloadContext:(NSDictionary *)payload {
@@ -440,23 +447,9 @@ static NSString * const kQmsRoutePayloadNotification = @"QmsRoutePayloadNotifica
440
447
  UIViewController *presenter = self.dashboardVC;
441
448
  if (!presenter) return;
442
449
 
443
- Class cls = NSClassFromString(@"DraftListingViewController");
444
- if (!cls) {
445
- NSLog(@"🧭 [DraftReminder] DraftListingViewController class not found");
446
- return;
447
- }
448
-
449
- UIViewController *vc = [[cls alloc] init];
450
- if (!vc) return;
451
-
452
- // Pass project name if available so the listing filters to the right project
453
- NSString *projectName = payload[@"project_name"];
454
- if (projectName.length > 0) {
455
- @try { [vc setValue:projectName forKey:@"projectName"]; }
456
- @catch (__unused NSException *e) {}
457
- }
458
-
459
- [self presentDeepLinkVC:vc];
450
+ // Delegate to QmsPluginUI which handles account switching + presentation.
451
+ // AccountManager and DraftListingViewController are both inside the framework.
452
+ [QmsPluginUI presentDraftListingFromPayload:payload fromViewController:presenter];
460
453
  }
461
454
 
462
455
  - (void)routePayloadIfNeeded:(NSDictionary *)payload force:(BOOL)force {
@@ -496,13 +489,12 @@ static NSString * const kQmsRoutePayloadNotification = @"QmsRoutePayloadNotifica
496
489
  } else if ([self isAppointmentType:type]) {
497
490
  NSLog(@"🧭 [QmsDashboardView] routing → presentAppointmentFromPayload (appointment type: %@)", type);
498
491
  [self presentAppointmentDetailsViewControllerWithPayload:payload title:title ?: @""];
499
- } else if ([type isEqualToString:@"clearance_letter"]) {
500
- NSLog(@"🧭 [QmsDashboardView] routing → presentDashboardFromPayload (clearance_letter)");
492
+ } else if ([self isDashboardType:type]) {
493
+ NSLog(@"🧭 [QmsDashboardView] routing → presentDashboardFromPayload (dashboard type: %@)", type);
501
494
  [self presentDashboardViewControllerWithPayload:payload title:title ?: @""];
502
- } else if ([self isDraftReminderType:type]) {
495
+ } else if ([type isEqualToString:@"draft_reminder"]) {
503
496
  NSLog(@"🧭 [QmsDashboardView] routing → presentDraftListingViewController (draft_reminder)");
504
- // [self presentDraftListingViewControllerWithPayload:payload];
505
- [self presentDashboardViewControllerWithPayload:payload title:title ?: @""];
497
+ [self presentDraftListingViewControllerWithPayload:payload];
506
498
  } else {
507
499
  NSLog(@"🧭 [QmsDashboardView] routePayloadIfNeeded — no route matched for type=%@", type ?: @"(none)");
508
500
  }
@@ -555,6 +547,12 @@ static NSString * const kQmsRoutePayloadNotification = @"QmsRoutePayloadNotifica
555
547
  - (void)applyHeaderThemeRecursivelyOnView:(UIView *)view textColor:(UIColor *)textColor iconColor:(UIColor *)iconColor {
556
548
  if (!view) return;
557
549
 
550
+ // Skip views tagged 9001 and their entire subtree — they manage their own colour.
551
+ if (view.tag == 9001) return;
552
+
553
+ // Never reach into UISegmentedControl internals.
554
+ if ([view isKindOfClass:[UISegmentedControl class]]) return;
555
+
558
556
  if (textColor && [view isKindOfClass:[UILabel class]]) {
559
557
  UILabel *label = (UILabel *)view;
560
558
  CGPoint originInRoot = [label.superview convertPoint:label.frame.origin toView:_dashboardVC.view];
@@ -599,6 +597,13 @@ static NSString * const kQmsRoutePayloadNotification = @"QmsRoutePayloadNotifica
599
597
  - (void)applyThemeRecursivelyOnView:(UIView *)view color:(UIColor *)themeColor {
600
598
  if (!view || !themeColor) return;
601
599
 
600
+ // Skip views tagged 9001 and their entire subtree — they manage their own colour.
601
+ if (view.tag == 9001) return;
602
+
603
+ // Never reach into UISegmentedControl internals — its tint/selected colour
604
+ // is set directly via selectedSegmentTintColor and must not be overwritten here.
605
+ if ([view isKindOfClass:[UISegmentedControl class]]) return;
606
+
602
607
  CGPoint originInRoot = [view.superview convertPoint:view.frame.origin toView:_dashboardVC.view];
603
608
  BOOL nearHeader = originInRoot.y <= 140.0 && view.bounds.size.height <= 120.0;
604
609
  if (nearHeader && [self isDefaultQmsBlueColor:view.backgroundColor]) {
@@ -655,6 +660,16 @@ static NSString * const kQmsRoutePayloadNotification = @"QmsRoutePayloadNotifica
655
660
  - (void)applyAccentRecursivelyOnView:(UIView *)view color:(UIColor *)color {
656
661
  if (!view || !color) return;
657
662
 
663
+ // Skip views (and their entire subtree) that have been tagged 9001 —
664
+ // this lets individual controls opt-out of theme colour replacement.
665
+ if (view.tag == 9001) return;
666
+
667
+ if ([view isKindOfClass:[UISegmentedControl class]]) {
668
+ // Never touch the internals of a UISegmentedControl — its
669
+ // selectedSegmentTintColor is managed by the VC that owns it.
670
+ return;
671
+ }
672
+
658
673
  if ([view isKindOfClass:[UIButton class]]) {
659
674
  UIButton *button = (UIButton *)view;
660
675
  button.tintColor = color;
@@ -809,7 +824,10 @@ RCT_EXPORT_MODULE();
809
824
  @"onAnalyticsScreen",
810
825
  @"onAnalyticsEvent",
811
826
  @"QmsSdkOnAnalyticsScreen",
812
- @"QmsSdkOnAnalyticsEvent"
827
+ @"QmsSdkOnAnalyticsEvent",
828
+ @"onDraftIssueCreated",
829
+ @"onDraftIssueDeleted",
830
+ @"onPoaSignOffCompleted"
813
831
  ];
814
832
  }
815
833
 
@@ -837,6 +855,18 @@ RCT_EXPORT_MODULE();
837
855
  selector:@selector(handleActiveAccountChangeEmitter:)
838
856
  name:@"QmsActiveAccountDidChangeNotification"
839
857
  object:nil];
858
+ [center addObserver:self
859
+ selector:@selector(handleDraftIssueCreated:)
860
+ name:@"QmsDraftIssueCreatedNotification"
861
+ object:nil];
862
+ [center addObserver:self
863
+ selector:@selector(handleDraftIssueDeleted:)
864
+ name:@"QmsDraftIssueDeletedNotification"
865
+ object:nil];
866
+ [center addObserver:self
867
+ selector:@selector(handlePoaSignOffCompleted:)
868
+ name:@"QmsPoaSignOffCompleted"
869
+ object:nil];
840
870
  }
841
871
 
842
872
  - (void)stopObserving {
@@ -866,6 +896,18 @@ RCT_EXPORT_MODULE();
866
896
  [self sendEventWithName:@"onActiveAccountChange" body:note.userInfo];
867
897
  }
868
898
 
899
+ - (void)handleDraftIssueCreated:(NSNotification *)note {
900
+ [self sendEventWithName:@"onDraftIssueCreated" body:note.userInfo];
901
+ }
902
+
903
+ - (void)handleDraftIssueDeleted:(NSNotification *)note {
904
+ [self sendEventWithName:@"onDraftIssueDeleted" body:note.userInfo];
905
+ }
906
+
907
+ - (void)handlePoaSignOffCompleted:(NSNotification *)note {
908
+ [self sendEventWithName:@"onPoaSignOffCompleted" body:note.userInfo ?: @{}];
909
+ }
910
+
869
911
  - (void)dealloc {
870
912
  [self stopObserving];
871
913
  }