@finspringinnovations/fixeddepositsdk 1.0.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 (311) hide show
  1. package/README.md +128 -0
  2. package/lib/api/applicationApi.d.ts +1 -0
  3. package/lib/api/applicationApi.js +11 -0
  4. package/lib/api/bankApi.d.ts +352 -0
  5. package/lib/api/bankApi.js +54 -0
  6. package/lib/api/baseApi.d.ts +6 -0
  7. package/lib/api/baseApi.js +406 -0
  8. package/lib/api/customerApi.d.ts +855 -0
  9. package/lib/api/customerApi.js +241 -0
  10. package/lib/api/fdApi.d.ts +979 -0
  11. package/lib/api/fdApi.js +112 -0
  12. package/lib/api/fdCalculatorApi.d.ts +179 -0
  13. package/lib/api/fdCalculatorApi.js +36 -0
  14. package/lib/api/index.d.ts +14 -0
  15. package/lib/api/index.js +45 -0
  16. package/lib/api/interestRateApi.d.ts +585 -0
  17. package/lib/api/interestRateApi.js +101 -0
  18. package/lib/api/kycApi.d.ts +486 -0
  19. package/lib/api/kycApi.js +71 -0
  20. package/lib/api/masterDataApi.d.ts +158 -0
  21. package/lib/api/masterDataApi.js +35 -0
  22. package/lib/api/nomineeApi.d.ts +325 -0
  23. package/lib/api/nomineeApi.js +46 -0
  24. package/lib/api/onboardingApi.d.ts +192 -0
  25. package/lib/api/onboardingApi.js +41 -0
  26. package/lib/api/panApi.d.ts +0 -0
  27. package/lib/api/panApi.js +23 -0
  28. package/lib/api/paymentApi.d.ts +325 -0
  29. package/lib/api/paymentApi.js +46 -0
  30. package/lib/api/workflowApi.d.ts +654 -0
  31. package/lib/api/workflowApi.js +90 -0
  32. package/lib/assets/images/images.d.ts +6 -0
  33. package/lib/assets/images/images.js +12 -0
  34. package/lib/components/AadhaarInput.d.ts +13 -0
  35. package/lib/components/AadhaarInput.js +47 -0
  36. package/lib/components/ActionButton.d.ts +12 -0
  37. package/lib/components/ActionButton.js +87 -0
  38. package/lib/components/ActiveFDCard.d.ts +18 -0
  39. package/lib/components/ActiveFDCard.js +116 -0
  40. package/lib/components/AmountInput.d.ts +20 -0
  41. package/lib/components/AmountInput.js +142 -0
  42. package/lib/components/CheckboxOption.d.ts +11 -0
  43. package/lib/components/CheckboxOption.js +41 -0
  44. package/lib/components/CompanyHeader.d.ts +7 -0
  45. package/lib/components/CompanyHeader.js +59 -0
  46. package/lib/components/DropdownSelector.d.ts +9 -0
  47. package/lib/components/DropdownSelector.js +49 -0
  48. package/lib/components/EmptyState.d.ts +17 -0
  49. package/lib/components/EmptyState.js +44 -0
  50. package/lib/components/ErrorDisplay.d.ts +17 -0
  51. package/lib/components/ErrorDisplay.js +69 -0
  52. package/lib/components/FAQItem.d.ts +9 -0
  53. package/lib/components/FAQItem.js +52 -0
  54. package/lib/components/FDCard.d.ts +23 -0
  55. package/lib/components/FDCard.js +116 -0
  56. package/lib/components/FormDropdown.d.ts +18 -0
  57. package/lib/components/FormDropdown.js +155 -0
  58. package/lib/components/FormSection.d.ts +14 -0
  59. package/lib/components/FormSection.js +38 -0
  60. package/lib/components/Header.d.ts +14 -0
  61. package/lib/components/Header.js +52 -0
  62. package/lib/components/IFSCSearchResultCard.d.ts +13 -0
  63. package/lib/components/IFSCSearchResultCard.js +70 -0
  64. package/lib/components/InfoBox.d.ts +8 -0
  65. package/lib/components/InfoBox.js +39 -0
  66. package/lib/components/InterestRateCard.d.ts +8 -0
  67. package/lib/components/InterestRateCard.js +46 -0
  68. package/lib/components/LoadingIndicator.d.ts +12 -0
  69. package/lib/components/LoadingIndicator.js +30 -0
  70. package/lib/components/OTPInput.d.ts +17 -0
  71. package/lib/components/OTPInput.js +144 -0
  72. package/lib/components/PaymentDetailsCard.d.ts +20 -0
  73. package/lib/components/PaymentDetailsCard.js +68 -0
  74. package/lib/components/PendingFDBottomSheet.d.ts +19 -0
  75. package/lib/components/PendingFDBottomSheet.js +122 -0
  76. package/lib/components/SafeAreaWrapper.d.ts +13 -0
  77. package/lib/components/SafeAreaWrapper.js +41 -0
  78. package/lib/components/ScreenHeader.d.ts +11 -0
  79. package/lib/components/ScreenHeader.js +46 -0
  80. package/lib/components/StatusDisplay.d.ts +15 -0
  81. package/lib/components/StatusDisplay.js +88 -0
  82. package/lib/components/TextFieldWithLabel.d.ts +46 -0
  83. package/lib/components/TextFieldWithLabel.js +326 -0
  84. package/lib/components/TrustBox.d.ts +8 -0
  85. package/lib/components/TrustBox.js +46 -0
  86. package/lib/components/ValidationErrorAlert.d.ts +23 -0
  87. package/lib/components/ValidationErrorAlert.js +39 -0
  88. package/lib/components/ValidationMessage.d.ts +9 -0
  89. package/lib/components/ValidationMessage.js +98 -0
  90. package/lib/components/index.d.ts +35 -0
  91. package/lib/components/index.js +64 -0
  92. package/lib/config/apiConfig.d.ts +34 -0
  93. package/lib/config/apiConfig.js +158 -0
  94. package/lib/config/appDataConfig.d.ts +159 -0
  95. package/lib/config/appDataConfig.js +310 -0
  96. package/lib/config/encryptionConfig.d.ts +21 -0
  97. package/lib/config/encryptionConfig.js +61 -0
  98. package/lib/config/workflowConstants.d.ts +62 -0
  99. package/lib/config/workflowConstants.js +63 -0
  100. package/lib/constants/strings/bank.d.ts +72 -0
  101. package/lib/constants/strings/bank.js +86 -0
  102. package/lib/constants/strings/base64Images.d.ts +26 -0
  103. package/lib/constants/strings/base64Images.js +29 -0
  104. package/lib/constants/strings/common.d.ts +53 -0
  105. package/lib/constants/strings/common.js +62 -0
  106. package/lib/constants/strings/employee.d.ts +61 -0
  107. package/lib/constants/strings/employee.js +77 -0
  108. package/lib/constants/strings/faq.d.ts +14 -0
  109. package/lib/constants/strings/faq.js +20 -0
  110. package/lib/constants/strings/fd.d.ts +122 -0
  111. package/lib/constants/strings/fd.js +151 -0
  112. package/lib/constants/strings/home.d.ts +49 -0
  113. package/lib/constants/strings/home.js +62 -0
  114. package/lib/constants/strings/index.d.ts +16 -0
  115. package/lib/constants/strings/index.js +44 -0
  116. package/lib/constants/strings/kyc.d.ts +80 -0
  117. package/lib/constants/strings/kyc.js +94 -0
  118. package/lib/constants/strings/nominee.d.ts +64 -0
  119. package/lib/constants/strings/nominee.js +81 -0
  120. package/lib/hooks/useAuth.d.ts +25 -0
  121. package/lib/hooks/useAuth.js +39 -0
  122. package/lib/hooks/useFDData.d.ts +11 -0
  123. package/lib/hooks/useFDData.js +40 -0
  124. package/lib/index.d.ts +48 -0
  125. package/lib/index.js +161 -0
  126. package/lib/navigation/RootNavigator.d.ts +9 -0
  127. package/lib/navigation/RootNavigator.js +459 -0
  128. package/lib/navigation/SimpleNavigator.d.ts +11 -0
  129. package/lib/navigation/SimpleNavigator.js +114 -0
  130. package/lib/navigation/helpers.d.ts +11 -0
  131. package/lib/navigation/helpers.js +83 -0
  132. package/lib/navigation/index.d.ts +17 -0
  133. package/lib/navigation/index.js +42 -0
  134. package/lib/navigation/types.d.ts +139 -0
  135. package/lib/navigation/types.js +2 -0
  136. package/lib/navigation/workflowNavigator.d.ts +22 -0
  137. package/lib/navigation/workflowNavigator.js +104 -0
  138. package/lib/providers/ApiProvider.d.ts +7 -0
  139. package/lib/providers/ApiProvider.js +16 -0
  140. package/lib/providers/MasterDataProvider.d.ts +10 -0
  141. package/lib/providers/MasterDataProvider.js +54 -0
  142. package/lib/screens/AadhaarVerification.d.ts +7 -0
  143. package/lib/screens/AadhaarVerification.js +627 -0
  144. package/lib/screens/AddBankAccount.d.ts +22 -0
  145. package/lib/screens/AddBankAccount.js +381 -0
  146. package/lib/screens/BankDetail.d.ts +16 -0
  147. package/lib/screens/BankDetail.js +596 -0
  148. package/lib/screens/Employee.d.ts +18 -0
  149. package/lib/screens/Employee.js +594 -0
  150. package/lib/screens/FDCalculator.d.ts +18 -0
  151. package/lib/screens/FDCalculator.js +767 -0
  152. package/lib/screens/FDList.d.ts +28 -0
  153. package/lib/screens/FDList.js +1853 -0
  154. package/lib/screens/FindIFSC.d.ts +16 -0
  155. package/lib/screens/FindIFSC.js +248 -0
  156. package/lib/screens/NomineeDetail.d.ts +17 -0
  157. package/lib/screens/NomineeDetail.js +592 -0
  158. package/lib/screens/PayNow.d.ts +14 -0
  159. package/lib/screens/PayNow.js +230 -0
  160. package/lib/screens/Payment.d.ts +11 -0
  161. package/lib/screens/Payment.js +191 -0
  162. package/lib/screens/PaymentStatus.d.ts +16 -0
  163. package/lib/screens/PaymentStatus.js +431 -0
  164. package/lib/screens/ReviewKYC.d.ts +23 -0
  165. package/lib/screens/ReviewKYC.js +727 -0
  166. package/lib/state/paymentSession.d.ts +8 -0
  167. package/lib/state/paymentSession.js +13 -0
  168. package/lib/store/fdListSelectedSlice.d.ts +21 -0
  169. package/lib/store/fdListSelectedSlice.js +26 -0
  170. package/lib/store/hooks.d.ts +8 -0
  171. package/lib/store/hooks.js +8 -0
  172. package/lib/store/index.d.ts +3 -0
  173. package/lib/store/index.js +8 -0
  174. package/lib/store/onboardingSlice.d.ts +12 -0
  175. package/lib/store/onboardingSlice.js +32 -0
  176. package/lib/store/store.d.ts +13 -0
  177. package/lib/store/store.js +27 -0
  178. package/lib/theme/ThemeContext.d.ts +210 -0
  179. package/lib/theme/ThemeContext.js +92 -0
  180. package/lib/theme/colors.d.ts +80 -0
  181. package/lib/theme/colors.js +85 -0
  182. package/lib/theme/index.d.ts +35 -0
  183. package/lib/theme/index.js +78 -0
  184. package/lib/theme/shadows.d.ts +53 -0
  185. package/lib/theme/shadows.js +58 -0
  186. package/lib/theme/typography.d.ts +134 -0
  187. package/lib/theme/typography.js +143 -0
  188. package/lib/types/dataTypes.d.ts +34 -0
  189. package/lib/types/dataTypes.js +2 -0
  190. package/lib/types/workflowTypes.d.ts +2 -0
  191. package/lib/types/workflowTypes.js +2 -0
  192. package/lib/utils/apiLogger.d.ts +48 -0
  193. package/lib/utils/apiLogger.js +105 -0
  194. package/lib/utils/encryption.d.ts +28 -0
  195. package/lib/utils/encryption.js +113 -0
  196. package/lib/utils/getFDData.d.ts +48 -0
  197. package/lib/utils/getFDData.js +145 -0
  198. package/lib/utils/globalData.d.ts +2 -0
  199. package/lib/utils/globalData.js +10 -0
  200. package/package.json +51 -0
  201. package/src/api/applicationApi.ts +12 -0
  202. package/src/api/bankApi.ts +42 -0
  203. package/src/api/baseApi.ts +463 -0
  204. package/src/api/customerApi.ts +324 -0
  205. package/src/api/fdApi.ts +150 -0
  206. package/src/api/fdCalculatorApi.ts +41 -0
  207. package/src/api/index.ts +29 -0
  208. package/src/api/interestRateApi.ts +143 -0
  209. package/src/api/kycApi.ts +63 -0
  210. package/src/api/masterDataApi.ts +37 -0
  211. package/src/api/nomineeApi.ts +34 -0
  212. package/src/api/onboardingApi.ts +64 -0
  213. package/src/api/panApi.ts +25 -0
  214. package/src/api/paymentApi.ts +34 -0
  215. package/src/api/workflowApi.ts +94 -0
  216. package/src/assets/images/Mahindra.png +0 -0
  217. package/src/assets/images/arrow-filled.png +0 -0
  218. package/src/assets/images/arrow-left.png +0 -0
  219. package/src/assets/images/backicon.png +0 -0
  220. package/src/assets/images/calendar.png +0 -0
  221. package/src/assets/images/chevron-down.png +0 -0
  222. package/src/assets/images/chevron-down@2x.png +0 -0
  223. package/src/assets/images/chevron-down@3x.png +0 -0
  224. package/src/assets/images/images.js +10 -0
  225. package/src/assets/images/shriram_logo.png +0 -0
  226. package/src/components/AadhaarInput.tsx +91 -0
  227. package/src/components/ActionButton.tsx +129 -0
  228. package/src/components/ActiveFDCard.tsx +188 -0
  229. package/src/components/AmountInput.tsx +217 -0
  230. package/src/components/CheckboxOption.tsx +93 -0
  231. package/src/components/CompanyHeader.tsx +80 -0
  232. package/src/components/DropdownSelector.tsx +77 -0
  233. package/src/components/EmptyState.tsx +109 -0
  234. package/src/components/ErrorDisplay.tsx +135 -0
  235. package/src/components/FAQItem.tsx +90 -0
  236. package/src/components/FDCard.tsx +186 -0
  237. package/src/components/FormDropdown.tsx +214 -0
  238. package/src/components/FormSection.tsx +86 -0
  239. package/src/components/Header.tsx +110 -0
  240. package/src/components/IFSCSearchResultCard.tsx +139 -0
  241. package/src/components/InfoBox.tsx +55 -0
  242. package/src/components/InterestRateCard.tsx +77 -0
  243. package/src/components/LoadingIndicator.tsx +63 -0
  244. package/src/components/OTPInput.tsx +213 -0
  245. package/src/components/PaymentDetailsCard.tsx +120 -0
  246. package/src/components/PendingFDBottomSheet.tsx +237 -0
  247. package/src/components/README.md +210 -0
  248. package/src/components/SafeAreaWrapper.tsx +68 -0
  249. package/src/components/ScreenHeader.tsx +83 -0
  250. package/src/components/StatusDisplay.tsx +139 -0
  251. package/src/components/TextFieldWithLabel.tsx +502 -0
  252. package/src/components/TrustBox.tsx +63 -0
  253. package/src/components/ValidationErrorAlert.tsx +57 -0
  254. package/src/components/ValidationMessage.tsx +134 -0
  255. package/src/components/index.tsx +47 -0
  256. package/src/config/apiConfig.ts +217 -0
  257. package/src/config/appDataConfig.ts +358 -0
  258. package/src/config/encryptionConfig.ts +65 -0
  259. package/src/config/workflowConstants.ts +70 -0
  260. package/src/constants/strings/README.md +146 -0
  261. package/src/constants/strings/bank.ts +92 -0
  262. package/src/constants/strings/base64Images.ts +31 -0
  263. package/src/constants/strings/common.ts +63 -0
  264. package/src/constants/strings/employee.ts +85 -0
  265. package/src/constants/strings/faq.ts +23 -0
  266. package/src/constants/strings/fd.ts +172 -0
  267. package/src/constants/strings/home.ts +67 -0
  268. package/src/constants/strings/index.ts +21 -0
  269. package/src/constants/strings/kyc.ts +100 -0
  270. package/src/constants/strings/nominee.ts +90 -0
  271. package/src/hooks/useAuth.ts +42 -0
  272. package/src/hooks/useFDData.ts +48 -0
  273. package/src/index.tsx +139 -0
  274. package/src/navigation/RootNavigator.tsx +687 -0
  275. package/src/navigation/SimpleNavigator.tsx +123 -0
  276. package/src/navigation/helpers.ts +85 -0
  277. package/src/navigation/index.tsx +84 -0
  278. package/src/navigation/types.ts +146 -0
  279. package/src/navigation/workflowNavigator.ts +131 -0
  280. package/src/providers/ApiProvider.tsx +23 -0
  281. package/src/providers/MasterDataProvider.tsx +30 -0
  282. package/src/screens/AadhaarVerification.tsx +809 -0
  283. package/src/screens/AddBankAccount.tsx +541 -0
  284. package/src/screens/BankDetail.tsx +826 -0
  285. package/src/screens/Employee.tsx +822 -0
  286. package/src/screens/FDCalculator.tsx +1002 -0
  287. package/src/screens/FDList.tsx +2199 -0
  288. package/src/screens/FindIFSC.tsx +332 -0
  289. package/src/screens/NomineeDetail.tsx +800 -0
  290. package/src/screens/PayNow.tsx +282 -0
  291. package/src/screens/Payment.tsx +224 -0
  292. package/src/screens/PaymentStatus.tsx +595 -0
  293. package/src/screens/ReviewKYC.tsx +1062 -0
  294. package/src/state/paymentSession.ts +13 -0
  295. package/src/store/fdListSelectedSlice.ts +42 -0
  296. package/src/store/hooks.ts +6 -0
  297. package/src/store/index.ts +3 -0
  298. package/src/store/onboardingSlice.ts +37 -0
  299. package/src/store/store.ts +27 -0
  300. package/src/theme/ThemeContext.tsx +84 -0
  301. package/src/theme/colors.ts +90 -0
  302. package/src/theme/index.ts +85 -0
  303. package/src/theme/shadows.ts +61 -0
  304. package/src/theme/typography.ts +151 -0
  305. package/src/types/dataTypes.ts +37 -0
  306. package/src/types/env.d.ts +93 -0
  307. package/src/types/workflowTypes.ts +12 -0
  308. package/src/utils/apiLogger.ts +166 -0
  309. package/src/utils/encryption.ts +159 -0
  310. package/src/utils/getFDData.ts +165 -0
  311. package/src/utils/globalData.ts +7 -0
@@ -0,0 +1,2199 @@
1
+ import React, { useState, useEffect, useCallback } from 'react';
2
+ import {
3
+ View,
4
+ Text,
5
+ StyleSheet,
6
+ ScrollView,
7
+ TouchableOpacity,
8
+ ActivityIndicator,
9
+ Alert,
10
+ Platform,
11
+ BackHandler,
12
+ StatusBar,
13
+ Image,
14
+ } from 'react-native';
15
+ import { useFocusEffect } from '@react-navigation/native';
16
+ import { Header, ActiveFDCard, FDCard, PendingFDBottomSheet } from '../components';
17
+ import { useColors, useTypography, useSpacing, useTheme } from '../theme/ThemeContext';
18
+ import { useGetInterestRatesMutation } from '../api/interestRateApi';
19
+ import { useGetCustomerApplicationsMutation, useGetCustomerApplicationDetailsMutation } from '../api/customerApi';
20
+ import { useGetMasterDataQuery } from '../api/masterDataApi';
21
+ import { useMasterData } from '../providers/MasterDataProvider';
22
+ import { useTerminateWorkflowMutation } from '../api/workflowApi';
23
+ import { usePaymentReverseFeedMutation } from '../api/fdApi';
24
+ import type { ColorScheme, ThemeName } from '../theme';
25
+ import SafeAreaWrapper from '../components/SafeAreaWrapper';
26
+ import { getUserInfoForAPI, getAppData, getEnvironmentData } from '../config/appDataConfig';
27
+ import { navigate } from '../navigation/helpers';
28
+ import { useAppDispatch, useAppSelector } from '../store';
29
+ import { setOnboardingIds } from '../store/onboardingSlice';
30
+ import { setFDListSelected } from '../store/fdListSelectedSlice';
31
+ import { handleWorkflowNavigation } from '../navigation/workflowNavigator';
32
+ import { WORKFLOW_STATES, SHRIRAM_WORKFLOW_STATES, MAHINDRA_WORKFLOW_STATES } from '../config/workflowConstants';
33
+ import { setGlobalData } from '../utils/globalData';
34
+ import { setGlobalData as setMahindraGlobalData } from '@finspringinnovations/mahindrafdsdk';
35
+ import { FD_STRINGS } from '../constants/strings';
36
+ import { base64Images } from '../constants/strings/base64Images';
37
+ import {
38
+ initializeSDK as initializeShriramSDK,
39
+ initializeEnvironment as initializeShriramEnvironment,
40
+ type AppData as ShriramAppData,
41
+ type EnvironmentData as ShriramEnvironmentData,
42
+ } from '@finspringinnovations/shriramfdsdk';
43
+ import {
44
+ initializeSDK as initializeMahindraSDK,
45
+ initializeEnvironment as initializeMahindraEnvironment,
46
+ type AppData as MahindraAppData,
47
+ type EnvironmentData as MahindraEnvironmentData,
48
+ } from '@finspringinnovations/mahindrafdsdk';
49
+ var completeFDData: any = null;
50
+
51
+ /** Resolve company logo type for FD list card by name or provider. */
52
+ function getLogoTypeForFD(fd: { name?: string; providerId?: string }): 'shriram' | 'mahindra' | undefined {
53
+ const name = (fd.name || '').toLowerCase();
54
+ const providerId = (fd.providerId || '').toLowerCase();
55
+ if (name.includes('shriram') || providerId.includes('shriram')) return 'shriram';
56
+ if (name.includes('mahindra') || providerId.includes('mahindra') || providerId.includes('881d0b98')) return 'mahindra';
57
+ return undefined;
58
+ }
59
+
60
+ /** Statuses that count as pending/active (in-progress) workflow. */
61
+ const PENDING_ACTIVE_STATUSES = ['active', 'pending', 'in_progress', 'in-progress'];
62
+
63
+ function getSdkTypeFromProviderId(
64
+ providerId: string,
65
+ mahindraProviderId: string,
66
+ shriramProviderId: string
67
+ ): 'mahindra' | 'shriram' | null {
68
+ const lower = (providerId || '').toLowerCase();
69
+ if (lower === mahindraProviderId.toLowerCase()) return 'mahindra';
70
+ if (lower === shriramProviderId.toLowerCase()) return 'shriram';
71
+ return null;
72
+ }
73
+
74
+ /** Get the pending/active application for a given provider, if any. */
75
+ function getPendingOrActiveAppForProvider(
76
+ applicationsData: any[],
77
+ providerId: string
78
+ ): any | null {
79
+ const providerIdLower = (providerId || '').toLowerCase();
80
+ return applicationsData.find((a: any) => {
81
+ const appProviderId = (a.fd_provider_id || a.fdProviderId || a.provider_id || a.providerId || '').toLowerCase();
82
+ if (appProviderId !== providerIdLower) return false;
83
+ const s = (a.wf_status || a.status || '').toString().toLowerCase();
84
+ return PENDING_ACTIVE_STATUSES.includes(s);
85
+ }) || null;
86
+ }
87
+
88
+ /** Convert application to pending FD display data. */
89
+ function appToPendingFDData(app: any): {
90
+ name: string;
91
+ invested: number;
92
+ value: number;
93
+ returns: number;
94
+ maturityDate: string;
95
+ interestPayout: string;
96
+ logoType?: 'shriram' | 'mahindra';
97
+ } {
98
+ const invested = Number(app.investment_amount ?? app.amount ?? app.investmentAmount ?? 0);
99
+ const rate = Number(app.interest_rate ?? app.interestRate ?? 0);
100
+ const value = Number(app.current_value ?? app.currentValue ?? app.maturity_amount ?? app.maturityAmount ?? 0);
101
+ const maturityDateStr: string = app.maturity_date || app.maturityDate || '';
102
+ let maturityDate = '--';
103
+ if (maturityDateStr) {
104
+ const d = new Date(maturityDateStr);
105
+ if (!isNaN(d.getTime())) {
106
+ const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
107
+ maturityDate = `${months[d.getMonth()]} ${d.getFullYear()}`;
108
+ }
109
+ }
110
+ const providerName = app.fd_provider_name || app.providerName || 'Mahindra Finance Ltd';
111
+ const logoType = getLogoTypeForFD({ name: providerName, providerId: app.fd_provider_id || app.fdProviderId || '' });
112
+ return {
113
+ name: providerName,
114
+ invested,
115
+ value,
116
+ returns: rate || 0,
117
+ maturityDate,
118
+ interestPayout: app.interest_payout_term || app.interestPayoutTerm || app.payout_term || 'Yearly',
119
+ logoType: logoType ?? undefined,
120
+ };
121
+ }
122
+
123
+ /** Resolve providerId from app payload robustly; infer from provider name when id key is missing. */
124
+ function resolveProviderIdFromApp(
125
+ app: any,
126
+ mahindraProviderId: string,
127
+ shriramProviderId: string
128
+ ): string {
129
+ const direct =
130
+ app?.fd_provider_id ||
131
+ app?.fdProviderId ||
132
+ app?.provider_id ||
133
+ app?.providerId ||
134
+ app?.fd_provider ||
135
+ app?.fdProvider ||
136
+ app?.provider;
137
+ if (direct) return String(direct);
138
+
139
+ const providerName = String(
140
+ app?.fd_provider_name ||
141
+ app?.fdProviderName ||
142
+ app?.provider_name ||
143
+ app?.providerName ||
144
+ ''
145
+ ).toLowerCase();
146
+
147
+ if (providerName.includes('mahindra')) return mahindraProviderId;
148
+ if (providerName.includes('shriram')) return shriramProviderId;
149
+ return '';
150
+ }
151
+
152
+ export interface FDItem {
153
+ id: string;
154
+ providerId: string; // Added providerId field
155
+ name: string;
156
+ accountNumber: string;
157
+ roi: string;
158
+ tenure: string;
159
+ amount: number;
160
+ maturityDate: string;
161
+ status: 'active' | 'matured' | 'pending';
162
+ creditRating: string;
163
+ interestPayout?: string; // Optional interest payout field
164
+ }
165
+
166
+ export interface FDListProps {
167
+ onGoBack?: () => void;
168
+ onSelectFD?: (fdId: string) => void;
169
+ onNavigateToFDCalculator?: (fdData?: any, masterData?: any) => void;
170
+ onNavigateToExternalSDK?: (sdkType: any, fdData?: any, context?: any) => void;
171
+ customStyles?: {
172
+ container?: object;
173
+ header?: object;
174
+ title?: object;
175
+ fdCard?: object;
176
+ };
177
+ }
178
+
179
+ const FDList: React.FC<FDListProps> = ({
180
+ onGoBack,
181
+ onSelectFD,
182
+ onNavigateToFDCalculator,
183
+ onNavigateToExternalSDK,
184
+ customStyles = {},
185
+ }) => {
186
+ const [activeTab, setActiveTab] = useState<string>(FD_STRINGS.ALL_FDS_TAB);
187
+ const [showPendingFDBottomSheet, setShowPendingFDBottomSheet] = useState(false);
188
+ const [selectedFD, setSelectedFD] = useState<FDItem | null>(null);
189
+ /** Application for which the pending bottom sheet is shown (matches the FD list card provider they tapped). Used for terminate/continue. */
190
+ const [pendingContextApp, setPendingContextApp] = useState<any>(null);
191
+ const [isInitialized, setIsInitialized] = useState(false);
192
+ /** Loading state for Mahindra customer/application/details prefetch (used for pending FD continue). */
193
+ const [isLoadingMahindraCustomerDetails, setIsLoadingMahindraCustomerDetails] = useState(false);
194
+ const colors = useColors();
195
+ const typography = useTypography();
196
+ const spacing = useSpacing();
197
+ const { themeName } = useTheme();
198
+ const dispatch = useAppDispatch();
199
+ const hasFetchedRatesRef = React.useRef(false);
200
+ const isCallingInterestRatesRef = React.useRef(false);
201
+ const MahindraProviderId = '881d0b98-78d7-4668-931c-f4f47d753e9d';
202
+ const ShriramProviderId = '92d94e74-c5cb-4141-91ad-ffa05df7fbff';
203
+
204
+ // API calls to fetch
205
+ const [getInterestRates, {
206
+ data: interestRates,
207
+ error: interestRatesError,
208
+ isLoading: isLoadingRates,
209
+ }] = useGetInterestRatesMutation();
210
+
211
+ const [getCustomerApplications, {
212
+ data: customerApplications,
213
+ error: customerApplicationsError,
214
+ isLoading: isLoadingApplications,
215
+ }] = useGetCustomerApplicationsMutation();
216
+ const [getCustomerApplicationDetails] = useGetCustomerApplicationDetailsMutation();
217
+
218
+ const [terminateWorkflow, {
219
+ data: terminateWorkflowData,
220
+ error: terminateWorkflowError,
221
+ isLoading: isTerminatingWorkflow,
222
+ }] = useTerminateWorkflowMutation();
223
+
224
+ // Payment Reverse Feed API
225
+ const [paymentReverseFeed, {
226
+ data: paymentReverseFeedResponse,
227
+ error: paymentReverseFeedError,
228
+ isLoading: isLoadingPaymentReverseFeed,
229
+ }] = usePaymentReverseFeedMutation();
230
+
231
+ const styles = createStyles(colors, typography, spacing, themeName);
232
+ const { masterData: masterDataContext, setMasterData } = useMasterData();
233
+
234
+ // Redux selectors for workflow IDs
235
+ const workflowInstanceId = useAppSelector((state: any) => state?.onboarding?.workflowInstanceId);
236
+ const applicationId = useAppSelector((state: any) => state?.onboarding?.applicationId);
237
+ const entityId = useAppSelector((state: any) => state?.onboarding?.entityid);
238
+ const providerId = useAppSelector((state: any) => state?.onboarding?.providerId);
239
+ const onboardingIds = useAppSelector((state: any) => state?.onboarding);
240
+
241
+ // Shared promise ref so FDList pending-flow and eager prefetch can wait
242
+ // on the same Mahindra customer/application/details fetch.
243
+ const mahindraDetailsPrefetchPromiseRef = React.useRef<Promise<void> | null>(null);
244
+
245
+ const initializeExternalSDKFromFDList = useCallback((
246
+ sdkType: 'shriram' | 'mahindra',
247
+ options?: { silent?: boolean }
248
+ ): boolean => {
249
+ const silent = options?.silent === true;
250
+ const currentAppData = getAppData();
251
+ const storedEnvData = getEnvironmentData();
252
+ const currentEnvData = storedEnvData as ShriramEnvironmentData & MahindraEnvironmentData | null;
253
+
254
+ if (!currentAppData) {
255
+ if (!silent) {
256
+ Alert.alert('Error', 'SDK app data not available. Please restart FD flow.');
257
+ }
258
+ return false;
259
+ }
260
+ if (!currentEnvData) {
261
+ if (!silent) {
262
+ Alert.alert('Error', 'SDK environment data not available. Please initialize environment from host app.');
263
+ }
264
+ return false;
265
+ }
266
+
267
+ try {
268
+ if (sdkType === 'shriram') {
269
+ initializeShriramEnvironment(currentEnvData as ShriramEnvironmentData);
270
+ initializeShriramSDK(currentAppData as ShriramAppData);
271
+ } else {
272
+ initializeMahindraEnvironment(currentEnvData as MahindraEnvironmentData);
273
+ initializeMahindraSDK(currentAppData as MahindraAppData);
274
+ }
275
+ return true;
276
+ } catch (error: any) {
277
+ if (!silent) {
278
+ Alert.alert(
279
+ 'Error',
280
+ `Failed to initialize ${sdkType === 'shriram' ? 'Shriram' : 'Mahindra'} SDK: ${error?.message || 'Unknown error'}`
281
+ );
282
+ }
283
+ return false;
284
+ }
285
+ }, []);
286
+
287
+ // As soon as FDList mounts, warm-initialize embedded SDKs with the same FDSDK data/env.
288
+ useEffect(() => {
289
+ initializeExternalSDKFromFDList('shriram', { silent: true });
290
+ initializeExternalSDKFromFDList('mahindra', { silent: true });
291
+ }, [initializeExternalSDKFromFDList]);
292
+
293
+ // Helper function to check if customer applications is empty
294
+ const isCustomerApplicationsEmpty = () => {
295
+ if (!customerApplications) {
296
+ return true;
297
+ }
298
+
299
+ // Handle direct array response
300
+ if (Array.isArray(customerApplications)) {
301
+ const isEmpty = customerApplications.length === 0;
302
+ return isEmpty;
303
+ }
304
+
305
+ // Handle response with data property
306
+ if (customerApplications.data && Array.isArray(customerApplications.data)) {
307
+ const isEmpty = customerApplications.data.length === 0;
308
+ return isEmpty;
309
+ }
310
+
311
+ // Handle response with applications property
312
+ if (customerApplications.applications && Array.isArray(customerApplications.applications)) {
313
+ const isEmpty = customerApplications.applications.length === 0;
314
+ return isEmpty;
315
+ }
316
+
317
+ // If customerApplications exists but doesn't match expected structure, consider it not empty
318
+ return false;
319
+ };
320
+
321
+ // Helper function to get provider ID from interest rates response (robust to varying shapes)
322
+ const getProviderIdFromInterestRates = () => {
323
+ try {
324
+ const data = interestRates?.data;
325
+ if (!data) return undefined;
326
+
327
+ const extractId = (rate: any) => {
328
+ return (
329
+ rate?.providerId ||
330
+ rate?.provider_id ||
331
+ rate?.fd_provider_id ||
332
+ rate?.fdProviderId ||
333
+ rate?.provider?.id ||
334
+ undefined
335
+ );
336
+ };
337
+
338
+ // Prefer SDR
339
+ const sdr = Array.isArray(data?.sdrScheme) ? data.sdrScheme : [];
340
+ for (const r of sdr) {
341
+ const id = extractId(r);
342
+ if (id) {
343
+ return id;
344
+ }
345
+ }
346
+
347
+ // Fallback to FDR
348
+ const fdr = Array.isArray(data?.fdrScheme) ? data.fdrScheme : [];
349
+ for (const r of fdr) {
350
+ const id = extractId(r);
351
+ if (id) {
352
+ return id;
353
+ }
354
+ }
355
+
356
+ return undefined;
357
+ } catch (e) {
358
+ return undefined;
359
+ }
360
+ };
361
+
362
+ /**
363
+ * Prefetch Mahindra customer/application/details for the currently pending/active
364
+ * Mahindra FD application. This is used to ensure that when we navigate into
365
+ * Mahindra SDK screens (especially via "Continue" on a pending FD), all
366
+ * customer details are already available, and we show a loader while fetching.
367
+ */
368
+ const prefetchMahindraCustomerApplicationDetails = useCallback(async (overrideMahindraApp?: any) => {
369
+ // Avoid duplicate fetches – reuse in-flight promise if any
370
+ if (mahindraDetailsPrefetchPromiseRef.current) {
371
+ return mahindraDetailsPrefetchPromiseRef.current;
372
+ }
373
+
374
+ const promise = (async () => {
375
+ try {
376
+ let mahindraApp: any = null;
377
+ const overrideProviderId = (
378
+ overrideMahindraApp?.fd_provider_id ||
379
+ overrideMahindraApp?.fdProviderId ||
380
+ overrideMahindraApp?.provider_id ||
381
+ overrideMahindraApp?.providerId ||
382
+ ''
383
+ ).toString();
384
+
385
+ if (overrideMahindraApp && overrideProviderId.toLowerCase() === MahindraProviderId.toLowerCase()) {
386
+ mahindraApp = overrideMahindraApp;
387
+ } else {
388
+ if (!customerApplications || isCustomerApplicationsEmpty()) {
389
+ return;
390
+ }
391
+
392
+ // Normalize list
393
+ let applicationsData: any[] = [];
394
+ if (Array.isArray(customerApplications)) {
395
+ applicationsData = customerApplications;
396
+ } else if (customerApplications.data && Array.isArray(customerApplications.data)) {
397
+ applicationsData = customerApplications.data;
398
+ } else if (customerApplications.applications && Array.isArray(customerApplications.applications)) {
399
+ applicationsData = customerApplications.applications;
400
+ }
401
+
402
+ if (!applicationsData.length) return;
403
+
404
+ // Find Mahindra pending/active app for MahindraProviderId
405
+ mahindraApp = getPendingOrActiveAppForProvider(applicationsData, MahindraProviderId);
406
+ }
407
+ if (!mahindraApp) return;
408
+
409
+ const ids = {
410
+ workflowInstanceId:
411
+ mahindraApp.workflow_instance_id ||
412
+ mahindraApp.workflowInstanceId ||
413
+ mahindraApp.workflowinstanceid ||
414
+ mahindraApp.workflow_instance ||
415
+ mahindraApp.workflowInstance ||
416
+ undefined,
417
+ applicationId:
418
+ mahindraApp.application_id ||
419
+ mahindraApp.applicationId ||
420
+ mahindraApp.applicationid ||
421
+ undefined,
422
+ entityid:
423
+ mahindraApp.entity_id ||
424
+ mahindraApp.entityId ||
425
+ mahindraApp.entityid ||
426
+ undefined,
427
+ customerId:
428
+ mahindraApp.customer_id ||
429
+ mahindraApp.customerId ||
430
+ mahindraApp.customerid ||
431
+ undefined,
432
+ providerId:
433
+ mahindraApp.fd_provider_id ||
434
+ mahindraApp.fdProviderId ||
435
+ mahindraApp.provider_id ||
436
+ mahindraApp.providerId ||
437
+ MahindraProviderId,
438
+ };
439
+
440
+ if (!ids.applicationId || !ids.customerId || !ids.providerId) {
441
+ return;
442
+ }
443
+
444
+ setIsLoadingMahindraCustomerDetails(true);
445
+
446
+ const userInfo = getUserInfoForAPI();
447
+ const detailsResp = await getCustomerApplicationDetails({
448
+ providerId: ids.providerId,
449
+ applicationId: ids.applicationId,
450
+ customerId: ids.customerId,
451
+ workflowInstanceId: ids.workflowInstanceId,
452
+ userreferenceid: userInfo.userReferenceId,
453
+ applicationid: ids.applicationId,
454
+ entityid: ids.entityid,
455
+ }).unwrap();
456
+
457
+ const detailsData = (detailsResp as any)?.data || detailsResp;
458
+ if (detailsData) {
459
+ setGlobalData({ mahindraCustomerDetails: detailsData });
460
+ setMahindraGlobalData({ mahindraCustomerDetails: detailsData });
461
+ }
462
+ } catch (err) {
463
+ } finally {
464
+ setIsLoadingMahindraCustomerDetails(false);
465
+ mahindraDetailsPrefetchPromiseRef.current = null;
466
+ }
467
+ })();
468
+
469
+ mahindraDetailsPrefetchPromiseRef.current = promise;
470
+ return promise;
471
+ }, [customerApplications, getCustomerApplicationDetails]);
472
+
473
+ // Helper function to filter FDs based on tenure
474
+ const filterFDsByTenure = (fdList: FDItem[], filter: string): FDItem[] => {
475
+ if (filter === FD_STRINGS.ALL_FDS_TAB) return fdList;
476
+
477
+ return fdList.filter(fd => {
478
+ // Extract tenure in months from the fd.tenure string (e.g., "12 Months" -> 12)
479
+ const tenureMatch = fd.tenure.match(/(\d+)\s*Months?/i);
480
+ if (!tenureMatch) return false;
481
+
482
+ const tenureMonths = parseInt(tenureMatch[1], 10);
483
+ const tenureYears = tenureMonths / 12;
484
+
485
+ switch (filter) {
486
+ case '<1Y':
487
+ return tenureYears <= 1;
488
+ case '<1-3Y':
489
+ return tenureYears > 1 && tenureYears <= 3;
490
+ case '<3-5Y':
491
+ return tenureYears > 3 && tenureYears <= 5;
492
+ case '>5Y':
493
+ return tenureYears > 5;
494
+ default:
495
+ return true;
496
+ }
497
+ });
498
+ };
499
+
500
+
501
+
502
+ // Derive identifiers for workflow termination from applications API (for active FD/provider)
503
+ const terminateIdentifiers = React.useMemo(() => {
504
+ try {
505
+ if (!customerApplications || isCustomerApplicationsEmpty()) {
506
+ return {
507
+ applicationId: undefined as string | undefined,
508
+ workflowInstanceId: undefined as string | undefined,
509
+ entityId: undefined as string | undefined,
510
+ providerIdFromApp: undefined as string | undefined,
511
+ };
512
+ }
513
+
514
+ // Normalize list
515
+ let applicationsData: any[] = [];
516
+ if (Array.isArray(customerApplications)) {
517
+ applicationsData = customerApplications;
518
+ } else if (customerApplications.data && Array.isArray(customerApplications.data)) {
519
+ applicationsData = customerApplications.data;
520
+ } else if (customerApplications.applications && Array.isArray(customerApplications.applications)) {
521
+ applicationsData = customerApplications.applications;
522
+ }
523
+
524
+ if (applicationsData.length === 0) {
525
+ return {
526
+ applicationId: undefined,
527
+ workflowInstanceId: undefined,
528
+ entityId: undefined,
529
+ providerIdFromApp: undefined,
530
+ };
531
+ }
532
+
533
+ // Prefer a pending/in-progress one for termination, else first
534
+ const appForTerminate = applicationsData.find((a: any) => {
535
+ const s = (a.wf_status || a.status || '').toString().toLowerCase();
536
+ return s === 'pending' || s === 'in_progress' || s === 'in-progress';
537
+ }) || applicationsData[0];
538
+
539
+ const applicationId: string | undefined =
540
+ appForTerminate.application_id || appForTerminate.applicationId || appForTerminate.id;
541
+ const workflowInstanceId: string | undefined =
542
+ appForTerminate.workflow_instance_id || appForTerminate.workflowInstanceId || appForTerminate.workflow?.instanceId;
543
+ const entityId: string | undefined =
544
+ appForTerminate.entity_id || appForTerminate.entityId || appForTerminate.entity?.id;
545
+ const providerIdFromApp: string | undefined =
546
+ appForTerminate.provider_id || appForTerminate.providerId || appForTerminate.fd_provider_id || appForTerminate.fdProviderId;
547
+
548
+ return { applicationId, workflowInstanceId, entityId, providerIdFromApp };
549
+ } catch (e) {
550
+ return {
551
+ applicationId: undefined,
552
+ workflowInstanceId: undefined,
553
+ entityId: undefined,
554
+ providerIdFromApp: undefined,
555
+ };
556
+ }
557
+ }, [customerApplications]);
558
+
559
+ const activeProviderId = terminateIdentifiers.providerIdFromApp;
560
+
561
+ // Master data fetch - prefer tapped FD provider, then active app provider, then rates
562
+ const defaultProviderId = ''; // Default provider ID for initial call
563
+ const inferredProviderId = React.useMemo(() => {
564
+ if (selectedFD?.providerId) return selectedFD.providerId;
565
+ if (activeProviderId) return activeProviderId;
566
+ if (interestRates?.data?.sdrScheme?.[0]?.providerId) return interestRates.data.sdrScheme[0].providerId;
567
+ if (interestRates?.data?.fdrScheme?.[0]?.providerId) return interestRates.data.fdrScheme[0].providerId;
568
+ return defaultProviderId;
569
+ }, [selectedFD, activeProviderId, interestRates]);
570
+
571
+ const { data: masterData, isLoading: isLoadingMaster } = useGetMasterDataQuery(
572
+ { providerId: inferredProviderId },
573
+ { skip: !inferredProviderId }
574
+ );
575
+ const { data: MahindraMasterData, refetch: refetchMahindraMasterData } = useGetMasterDataQuery(
576
+ { providerId: MahindraProviderId },
577
+ { skip: false }
578
+ );
579
+ const { data: ShriramMasterData, refetch: refetchShriramMasterData } = useGetMasterDataQuery(
580
+ { providerId: ShriramProviderId },
581
+ { skip: false }
582
+ );
583
+
584
+ // Only render once all three API calls have completed (success or error)
585
+ // Show loading initially until all APIs have completed
586
+ const isAllDataReady = React.useMemo(() => {
587
+ // If not initialized yet, show loading
588
+ if (!isInitialized) return false;
589
+
590
+ // Step 1: Check if interest rates API has been called and completed
591
+ // If currently loading, show loading
592
+ if (isLoadingRates) return false;
593
+ // If not loading but no data and no error, API hasn't been called yet - show loading
594
+ if (!interestRates && !interestRatesError) return false;
595
+
596
+ // Step 2: Once interest rates are done, customer applications should be called
597
+ // If currently loading, show loading
598
+ if (isLoadingApplications) return false;
599
+ // If interest rates succeeded, we must wait for customer applications
600
+ // If interest rates failed, we can still proceed (but applications might not be called)
601
+ if (interestRates) {
602
+ // Interest rates succeeded, so applications should be called
603
+ // If not loading but no data and no error, applications haven't been called yet - show loading
604
+ if (!customerApplications && !customerApplicationsError) return false;
605
+ }
606
+
607
+ // Step 3: Check master data - only required if providerId is available
608
+ const masterShouldRun = !!inferredProviderId;
609
+ if (masterShouldRun) {
610
+ // Master data should be done (either loaded or error, but not loading)
611
+ if (isLoadingMaster) return false;
612
+ }
613
+
614
+ return true;
615
+ }, [isInitialized, interestRates, interestRatesError, isLoadingRates, customerApplications, customerApplicationsError, isLoadingApplications, inferredProviderId, isLoadingMaster]);
616
+
617
+ useEffect(() => {
618
+ if (masterData) {
619
+ setMasterData(masterData);
620
+ }
621
+ }, [masterData, setMasterData]);
622
+
623
+ // Function to refresh all data - Required sequence: Interest -> Applications -> Master
624
+ const refreshData = useCallback(async () => {
625
+ if (!isInitialized) {
626
+ return;
627
+ }
628
+
629
+ // Prevent multiple simultaneous calls
630
+ if (isCallingInterestRatesRef.current || isLoadingRates) {
631
+ return;
632
+ }
633
+
634
+ try {
635
+ isCallingInterestRatesRef.current = true;
636
+ // Add 500ms delay before calling interest rate API
637
+ await new Promise(resolve => setTimeout(resolve, 500));
638
+ await getInterestRates({});
639
+ } catch (error) {
640
+ // Handle error silently
641
+ } finally {
642
+ isCallingInterestRatesRef.current = false;
643
+ }
644
+ }, [getInterestRates, isInitialized, isLoadingRates]);
645
+
646
+ // Initialize component after mount
647
+ useEffect(() => {
648
+ const initializeComponent = async () => {
649
+ // Validate required user data before proceeding
650
+ const appData = getAppData();
651
+
652
+ const requiredFields = [
653
+ { field: 'id', label: 'User ID' },
654
+ { field: 'name', label: 'User Name' },
655
+ { field: 'dob', label: 'Date of Birth' },
656
+ { field: 'gender', label: 'Gender' },
657
+ { field: 'mobNo', label: 'Mobile Number' },
658
+ { field: 'email', label: 'Email' },
659
+ ];
660
+
661
+ const missingFields: string[] = [];
662
+
663
+ requiredFields.forEach(({ field, label }) => {
664
+ const value = appData?.[field as keyof typeof appData];
665
+ if (!value || (typeof value === 'string' && !value.trim())) {
666
+ missingFields.push(label);
667
+ }
668
+ });
669
+
670
+ // Show alert if required fields are missing
671
+ if (missingFields.length > 0) {
672
+ Alert.alert(
673
+ 'Cannot Proceed with FD Booking',
674
+ `The following required information is missing from the main app:\n\n${missingFields.join('\n')}\n\nPlease provide all required user information to proceed.`,
675
+ [
676
+ {
677
+ text: 'Cancel FD Booking',
678
+ onPress: () => {
679
+ // Redirect back to main app
680
+ if (onGoBack) {
681
+ onGoBack();
682
+ } else {
683
+ // No-op when onGoBack is not provided
684
+ }
685
+ },
686
+ style: 'cancel',
687
+ },
688
+ ],
689
+ { cancelable: false }
690
+ );
691
+ return; // Don't initialize if validation fails
692
+ }
693
+
694
+ // Additional value validation (case-insensitive): gender, maritalStatus, typeOfAccount
695
+ const errors: string[] = [];
696
+
697
+ // Gender must be Male, Female, or Other (case-insensitive)
698
+ const gender = String(appData?.gender || '').trim().toLowerCase();
699
+ const allowedGenders = ['male', 'female', 'other'];
700
+ if (gender && !allowedGenders.includes(gender)) {
701
+ errors.push("Gender must be one of: Male, Female, Other");
702
+ }
703
+
704
+ // Marital status must be Married, Unmarried, or Other (case-insensitive)
705
+ const maritalStatus = String(appData?.maritalStatus || '').trim().toLowerCase();
706
+ const allowedMarital = ['married', 'unmarried', 'other'];
707
+ if (maritalStatus && !allowedMarital.includes(maritalStatus)) {
708
+ errors.push("Marital Status must be one of: Married, Unmarried, Other");
709
+ }
710
+
711
+ // Account type must be 'Saving A/c' or 'Current A/c' (support legacy codes like SB/CA)
712
+ const typeOfAccountRaw = String(appData?.typeOfAccount || '').trim();
713
+ const normalizeAccountType = (value: string) =>
714
+ value.replace(/[^a-z]/gi, '').toLowerCase();
715
+ const accountTypeMap: Record<string, 'Saving A/c' | 'Current A/c'> = {
716
+ savingac: 'Saving A/c',
717
+ savingaccount: 'Saving A/c',
718
+ savingsaccount: 'Saving A/c',
719
+ savings: 'Saving A/c',
720
+ saving: 'Saving A/c',
721
+ sb: 'Saving A/c',
722
+ currentac: 'Current A/c',
723
+ currentaccount: 'Current A/c',
724
+ current: 'Current A/c',
725
+ ca: 'Current A/c',
726
+ };
727
+ const normalizedAccountType = normalizeAccountType(typeOfAccountRaw);
728
+ const canonicalAccountType = accountTypeMap[normalizedAccountType];
729
+ if (typeOfAccountRaw && !canonicalAccountType) {
730
+ errors.push("Type of Account must be one of: 'Saving A/c', 'Current A/c'");
731
+ }
732
+
733
+ if (errors.length > 0) {
734
+ Alert.alert(
735
+ 'Invalid User Data',
736
+ `${errors.join('\n')}`,
737
+ [
738
+ {
739
+ text: 'OK',
740
+ onPress: () => {
741
+ if (onGoBack) {
742
+ onGoBack();
743
+ }
744
+ },
745
+ style: 'cancel',
746
+ },
747
+ ],
748
+ { cancelable: false }
749
+ );
750
+ return; // Stop initialization on invalid values
751
+ }
752
+
753
+ // Small delay to ensure component is fully mounted
754
+ await new Promise(resolve => setTimeout(resolve, 100));
755
+ setIsInitialized(true);
756
+ };
757
+
758
+ initializeComponent();
759
+ }, [onGoBack]);
760
+
761
+ // Handle Android hardware back button - use same navigation as header back button
762
+ useEffect(() => {
763
+ if (Platform.OS !== 'android') return;
764
+
765
+ const onHardwareBackPress = () => {
766
+ // Call onGoBack which triggers onExit in RootNavigator
767
+ if (onGoBack) {
768
+ onGoBack();
769
+ }
770
+ return true; // Prevent default behavior
771
+ };
772
+
773
+ const backHandler = BackHandler.addEventListener(
774
+ 'hardwareBackPress',
775
+ onHardwareBackPress
776
+ );
777
+
778
+ return () => backHandler.remove();
779
+ }, [onGoBack]);
780
+
781
+ // Removed init-time fetch to avoid duplicate calls; we'll fetch on first focus only
782
+
783
+ // Refresh data when screen comes into focus (only if initialized)
784
+ useFocusEffect(
785
+ useCallback(() => {
786
+ if (!isInitialized) return;
787
+ if (hasFetchedRatesRef.current) return; // ensure called only once when screen first shows
788
+ // Prevent multiple simultaneous calls
789
+ if (isCallingInterestRatesRef.current || isLoadingRates) return;
790
+ hasFetchedRatesRef.current = true;
791
+ refreshData();
792
+ }, [refreshData, isInitialized, isLoadingRates])
793
+ );
794
+
795
+ // As soon as FDList has customer applications, eagerly prefetch Mahindra
796
+ // customer/application/details so that if user taps a Mahindra pending FD,
797
+ // details are already cached (or at least being fetched with a loader).
798
+ useEffect(() => {
799
+ if (!isInitialized) return;
800
+ if (!customerApplications || isCustomerApplicationsEmpty()) return;
801
+ prefetchMahindraCustomerApplicationDetails();
802
+ }, [isInitialized, customerApplications, prefetchMahindraCustomerApplicationDetails]);
803
+
804
+ // After interest rates are received, call Customer Applications
805
+ useEffect(() => {
806
+ if (!interestRates) return;
807
+ try {
808
+ const userInfo = getUserInfoForAPI();
809
+ getCustomerApplications({ userReferenceId: userInfo.id });
810
+ } catch (e) {
811
+ }
812
+ // eslint-disable-next-line react-hooks/exhaustive-deps
813
+ }, [!!interestRates]);
814
+
815
+ // Handle API errors
816
+ useEffect(() => {
817
+ if (interestRatesError) {
818
+ Alert.alert(
819
+ 'API Error',
820
+ 'Failed to fetch interest rates. Please try again.',
821
+ [
822
+ {
823
+ text: 'Retry',
824
+ onPress: async () => {
825
+ // Prevent multiple simultaneous calls
826
+ if (isCallingInterestRatesRef.current || isLoadingRates) {
827
+ return;
828
+ }
829
+ try {
830
+ isCallingInterestRatesRef.current = true;
831
+ // Add 500ms delay before retrying interest rate API
832
+ await new Promise(resolve => setTimeout(resolve, 500));
833
+ await getInterestRates({});
834
+ } catch (error) {
835
+ // Handle error silently
836
+ } finally {
837
+ isCallingInterestRatesRef.current = false;
838
+ }
839
+ }
840
+ },
841
+ { text: 'Continue', style: 'cancel' }
842
+ ]
843
+ );
844
+ }
845
+ }, [interestRatesError, getInterestRates]);
846
+
847
+ // Log successful data fetch
848
+ useEffect(() => {
849
+ if (interestRates && interestRates.data?.sdrScheme?.length > 0) {
850
+ // Interest rates loaded successfully
851
+ }
852
+ }, [interestRates]);
853
+
854
+ // Persist providerId to onboarding store as soon as we have rates
855
+ // useEffect(() => {
856
+ // const pid = getProviderIdFromInterestRates();
857
+ // if (pid) {
858
+ // try {
859
+ // dispatch(setOnboardingIds({ providerId: pid }));
860
+ // } catch (e) {
861
+ // // Handle error silently
862
+ // }
863
+ // }
864
+ // }, [interestRates, dispatch]);
865
+
866
+ // useEffect(() => {
867
+ // if (customerApplications) {
868
+ // // Customer applications loaded successfully
869
+
870
+ // // Persist onboarding identifiers globally for subsequent API calls (like FDCalculator)
871
+ // try {
872
+ // // Extract workflow parameters from response
873
+ // const responseData = customerApplications?.data || customerApplications;
874
+ // let applicationData = Array.isArray(responseData) ? responseData[0] : responseData;
875
+
876
+ // // Prefer the application with wf_status === 'Active'
877
+ // if (Array.isArray(responseData)) {
878
+ // const activeOnly = responseData.find((app: any) => {
879
+ // const status = (app.wf_status || app.status || '').toString();
880
+ // return status === 'Active';
881
+ // });
882
+ // if (activeOnly) applicationData = activeOnly;
883
+ // }
884
+
885
+ // if (Array.isArray(responseData)) {
886
+ // completeFDData = responseData.find((app: any) => {
887
+ // const status = (app.wf_status || app.status || '').toString();
888
+ // return status === 'Completed';
889
+ // });
890
+ // setGlobalData({ completeFDData: !!completeFDData });
891
+ // }
892
+ // if (applicationData) {
893
+ // const ids = {
894
+ // workflowInstanceId: applicationData.workflow_instance_id,
895
+ // applicationId: applicationData.application_id,
896
+ // entityid: applicationData.entity_id,
897
+ // customerId: applicationData.customer_id,
898
+ // fdId: applicationData.fd_id,
899
+ // currentState: applicationData.current_state,
900
+ // currentTask: applicationData.current_task,
901
+ // // Persist providerId from interest-rate API (prefers SDR, fallback FDR)
902
+ // providerId: getProviderIdFromInterestRates(),
903
+ // wfStatus: applicationData.wf_status,
904
+ // };
905
+ // if (dispatch && setOnboardingIds) {
906
+ // dispatch(setOnboardingIds(ids));
907
+ // } else {
908
+ // // Dispatch or setOnboardingIds unavailable
909
+ // }
910
+ // }
911
+ // } catch (error) {
912
+ // // Handle error silently
913
+ // }
914
+ // }
915
+ // }, [customerApplications, dispatch]);
916
+
917
+ // Unified handler: behave like bottom sheet Continue flow
918
+ const handlePendingFDContinue = async (
919
+ overrideApp?: any,
920
+ forcedSdkType?: 'mahindra' | 'shriram'
921
+ ) => {
922
+ // Get current state from customer applications for workflow navigation
923
+ let currentState: any = null;
924
+ let transactionId: string | undefined = undefined;
925
+ let appData: any = overrideApp || pendingContextApp;
926
+
927
+ let fdDisplay: ReturnType<typeof appToPendingFDData> | null = null;
928
+ let onboardingIdsFromApp: any = null;
929
+
930
+ try {
931
+ if (!appData && customerApplications && !isCustomerApplicationsEmpty()) {
932
+ // Normalize list
933
+ let applicationsData: any[] = [];
934
+ if (Array.isArray(customerApplications)) {
935
+ applicationsData = customerApplications;
936
+ } else if (customerApplications.data && Array.isArray(customerApplications.data)) {
937
+ applicationsData = customerApplications.data;
938
+ } else if (customerApplications.applications && Array.isArray(customerApplications.applications)) {
939
+ applicationsData = customerApplications.applications;
940
+ }
941
+
942
+ if (applicationsData.length > 0) {
943
+ // Fallback: find first Active application when no override/pendingContext
944
+ appData = applicationsData.find((a: any) => {
945
+ const s = (a.wf_status || a.status || '').toString();
946
+ return s === 'Active';
947
+ }) || applicationsData.find((a: any) => {
948
+ const s = (a.wf_status || a.status || '').toString().toLowerCase();
949
+ return PENDING_ACTIVE_STATUSES.includes(s);
950
+ }) || applicationsData[0];
951
+ }
952
+ }
953
+ if (appData) {
954
+ currentState = appData.current_state;
955
+ transactionId = appData.transaction_id || appData.transactionId;
956
+ fdDisplay = appToPendingFDData(appData);
957
+ let providerIdFromApp = resolveProviderIdFromApp(appData, MahindraProviderId, ShriramProviderId);
958
+ if (forcedSdkType === 'mahindra') {
959
+ providerIdFromApp = MahindraProviderId;
960
+ } else if (forcedSdkType === 'shriram') {
961
+ providerIdFromApp = ShriramProviderId;
962
+ }
963
+ const providerIdLower = (providerIdFromApp || '').toLowerCase();
964
+ const isMahindra = providerIdLower === MahindraProviderId.toLowerCase();
965
+
966
+ // For Mahindra flows, ensure customer/application/details are fully loaded
967
+ // before navigating into ExternalSDK so downstream screens see complete
968
+ // nominee/bank/KYC/guardian data. Show a loader while we wait.
969
+ if (isMahindra) {
970
+ await prefetchMahindraCustomerApplicationDetails(appData);
971
+ }
972
+
973
+ try {
974
+ const fdListSelectedData = {
975
+ id: appData.fd_id || appData.fdId || 'active-fd',
976
+ providerId: providerIdFromApp,
977
+ name: fdDisplay.name,
978
+ accountNumber: appData.account_number || appData.accountNumber || '',
979
+ roi: `${fdDisplay.returns}% p.a.`,
980
+ tenure: fdDisplay.maturityDate || '-',
981
+ amount: Number(fdDisplay.invested) || 0,
982
+ maturityDate: fdDisplay.maturityDate || '-',
983
+ status: 'active' as const,
984
+ creditRating: appData.credit_rating || appData.creditRating || selectedFD?.creditRating || 'NA',
985
+ companyName: fdDisplay.name,
986
+ fdRate: `${fdDisplay.returns}% p.a.`,
987
+ interestPayout: fdDisplay.interestPayout || '',
988
+ };
989
+ dispatch(setFDListSelected(fdListSelectedData));
990
+ } catch (e) {
991
+ // Handle error silently
992
+ }
993
+
994
+ try {
995
+ const ids = {
996
+ workflowInstanceId: appData.workflow_instance_id || appData.workflowInstanceId,
997
+ applicationId: appData.application_id || appData.applicationId,
998
+ entityid: appData.entity_id || appData.entityId,
999
+ customerId: appData.customer_id || appData.customerId,
1000
+ fdId: appData.fd_id || appData.fdId,
1001
+ currentState: appData.current_state || appData.currentState,
1002
+ currentTask: appData.current_task || appData.currentTask,
1003
+ providerId: providerIdFromApp || undefined,
1004
+ wfStatus: appData.wf_status || appData.wfStatus,
1005
+ };
1006
+ onboardingIdsFromApp = ids;
1007
+ dispatch(setOnboardingIds(ids));
1008
+ } catch (e) {
1009
+ }
1010
+ }
1011
+ } catch (error) {
1012
+ // Handle error silently
1013
+ }
1014
+
1015
+ if (!fdDisplay && appData) fdDisplay = appToPendingFDData(appData);
1016
+
1017
+ // If current state is payment and transactionId is available, call payment reverse feed API
1018
+ // Check for both Mahindra and Shriram payment states
1019
+ const isPaymentState = currentState === WORKFLOW_STATES.PAYMENT ||
1020
+ currentState === MAHINDRA_WORKFLOW_STATES.PAYMENT ||
1021
+ currentState === SHRIRAM_WORKFLOW_STATES.PAYMENT;
1022
+
1023
+ if (isPaymentState && transactionId) {
1024
+ try {
1025
+ // Get user info and required IDs (prefer appData from continue context)
1026
+ const userInfo = getUserInfoForAPI();
1027
+ let providerIdFromApp = resolveProviderIdFromApp(appData, MahindraProviderId, ShriramProviderId);
1028
+ if (forcedSdkType === 'mahindra') providerIdFromApp = MahindraProviderId;
1029
+ if (forcedSdkType === 'shriram') providerIdFromApp = ShriramProviderId;
1030
+ const paymentReverseFeedRequest = {
1031
+ providerId: providerIdFromApp || providerId,
1032
+ workflowInstanceId: appData?.workflow_instance_id || appData?.workflowInstanceId || workflowInstanceId,
1033
+ userreferenceid: userInfo.id,
1034
+ applicationid: appData?.application_id || appData?.applicationId || applicationId,
1035
+ entityid: appData?.entity_id || appData?.entityId || entityId,
1036
+ transactionId: transactionId,
1037
+ };
1038
+
1039
+ const response = await paymentReverseFeed(paymentReverseFeedRequest).unwrap();
1040
+
1041
+ // Handle the response based on payment status
1042
+ const paymentStatus = (response?.data?.paymentStatus || '').toLowerCase();
1043
+ const statusParam = paymentStatus === 'success' ? 'success' : paymentStatus === 'failed' ? 'failed' : 'pending';
1044
+
1045
+ const fdDataParam = fdDisplay ? {
1046
+ companyName: fdDisplay.name,
1047
+ amount: Number(fdDisplay.invested) || 0,
1048
+ fdRate: `${fdDisplay.returns}% p.a.`,
1049
+ tenure: fdDisplay.maturityDate ? `${fdDisplay.maturityDate}` : '-',
1050
+ interestPayout: fdDisplay.interestPayout || 'Yearly',
1051
+ } : activeFD ? {
1052
+ companyName: activeFD.name,
1053
+ amount: Number(activeFD.invested) || 0,
1054
+ fdRate: `${activeFD.returns}% p.a.`,
1055
+ tenure: activeFD.maturityDate ? `${activeFD.maturityDate}` : '-',
1056
+ interestPayout: activeFD.interestPayout || 'Yearly',
1057
+ } : undefined;
1058
+
1059
+ // Shriram: always navigate to Shriram SDK PaymentStatus, never FDSDK PaymentStatus
1060
+ let paymentProviderId = resolveProviderIdFromApp(appData, MahindraProviderId, ShriramProviderId).toLowerCase();
1061
+ if (forcedSdkType === 'mahindra') paymentProviderId = MahindraProviderId.toLowerCase();
1062
+ if (forcedSdkType === 'shriram') paymentProviderId = ShriramProviderId.toLowerCase();
1063
+ const isShriramPayment = paymentProviderId === ShriramProviderId.toLowerCase();
1064
+ if (isShriramPayment && appData) {
1065
+ if (!initializeExternalSDKFromFDList('shriram')) return;
1066
+ let ShriramSDKGlobalDataPayment: any = null;
1067
+ if (customerApplications && !isCustomerApplicationsEmpty()) {
1068
+ let applicationsData: any[] = [];
1069
+ if (Array.isArray(customerApplications)) applicationsData = customerApplications;
1070
+ else if (customerApplications?.data && Array.isArray(customerApplications.data)) applicationsData = customerApplications.data;
1071
+ else if (customerApplications?.applications && Array.isArray(customerApplications.applications)) applicationsData = customerApplications.applications;
1072
+ const completeApplications = applicationsData.filter((app: any) => {
1073
+ const s = (app.wf_status || app.status || '').toString().toLowerCase();
1074
+ return s === 'completed' || s === 'complete';
1075
+ });
1076
+ const shriramComplete = completeApplications.find((app: any) => (app.fd_provider_id || app.fdProviderId || app.provider_id || app.providerId || '').toLowerCase() === ShriramProviderId.toLowerCase());
1077
+ if (shriramComplete) ShriramSDKGlobalDataPayment = shriramComplete;
1078
+ }
1079
+ const fdListSelectedDataPayment = activeFD ? {
1080
+ id: appData.fd_id || appData.fdId || 'active-fd',
1081
+ providerId: providerIdFromApp,
1082
+ name: activeFD.name,
1083
+ accountNumber: appData.account_number || appData.accountNumber || '',
1084
+ roi: `${activeFD.returns}% p.a.`,
1085
+ tenure: activeFD.maturityDate || '-',
1086
+ amount: Number(activeFD.invested) || 0,
1087
+ maturityDate: activeFD.maturityDate || '-',
1088
+ status: 'active' as const,
1089
+ creditRating: appData.credit_rating || appData.creditRating || 'NA',
1090
+ companyName: activeFD.name,
1091
+ fdRate: `${activeFD.returns}% p.a.`,
1092
+ interestPayout: activeFD.interestPayout || 'Yearly',
1093
+ } : null;
1094
+ const masterDataPayment = ShriramMasterData;
1095
+
1096
+ navigate('ExternalSDK', {
1097
+ sdkType: 'shriram',
1098
+ providerId: providerIdFromApp,
1099
+ fdListSelectedData: fdListSelectedDataPayment,
1100
+ masterData: masterDataPayment,
1101
+ onboardingIds: onboardingIdsFromApp || onboardingIds,
1102
+ initialRouteName: 'PaymentStatus',
1103
+ initialPaymentStatusParams: { status: statusParam, transactionId, fdData: fdDataParam },
1104
+ shriramSDKGlobalData: ShriramSDKGlobalDataPayment,
1105
+ } as any);
1106
+ return;
1107
+ }
1108
+
1109
+ navigate('PaymentStatus', { status: statusParam, transactionId, fdData: fdDataParam } as any);
1110
+ return;
1111
+
1112
+ } catch (error) {
1113
+ // Handle error silently
1114
+ const fdDataParam = fdDisplay ? {
1115
+ companyName: fdDisplay.name,
1116
+ amount: Number(fdDisplay.invested) || 0,
1117
+ fdRate: `${fdDisplay.returns}% p.a.`,
1118
+ tenure: fdDisplay.maturityDate ? `${fdDisplay.maturityDate}` : '-',
1119
+ interestPayout: fdDisplay.interestPayout || 'Yearly',
1120
+ } : activeFD ? {
1121
+ companyName: activeFD.name,
1122
+ amount: Number(activeFD.invested) || 0,
1123
+ fdRate: `${activeFD.returns}% p.a.`,
1124
+ tenure: activeFD.maturityDate ? `${activeFD.maturityDate}` : '-',
1125
+ interestPayout: activeFD.interestPayout || 'Yearly',
1126
+ } : undefined;
1127
+ let providerIdFromApp = resolveProviderIdFromApp(appData, MahindraProviderId, ShriramProviderId);
1128
+ if (forcedSdkType === 'mahindra') providerIdFromApp = MahindraProviderId;
1129
+ if (forcedSdkType === 'shriram') providerIdFromApp = ShriramProviderId;
1130
+ const paymentProviderIdCatch = providerIdFromApp.toLowerCase();
1131
+ const isShriramPaymentCatch = paymentProviderIdCatch === ShriramProviderId.toLowerCase();
1132
+ if (isShriramPaymentCatch && appData) {
1133
+ if (!initializeExternalSDKFromFDList('shriram')) return;
1134
+ let ShriramSDKGlobalDataPayment: any = null;
1135
+ if (customerApplications && !isCustomerApplicationsEmpty()) {
1136
+ let applicationsData: any[] = [];
1137
+ if (Array.isArray(customerApplications)) applicationsData = customerApplications;
1138
+ else if (customerApplications?.data && Array.isArray(customerApplications.data)) applicationsData = customerApplications.data;
1139
+ else if (customerApplications?.applications && Array.isArray(customerApplications.applications)) applicationsData = customerApplications.applications;
1140
+ const completeApplications = applicationsData.filter((app: any) => {
1141
+ const s = (app.wf_status || app.status || '').toString().toLowerCase();
1142
+ return s === 'completed' || s === 'complete';
1143
+ });
1144
+ const shriramComplete = completeApplications.find((app: any) => (app.fd_provider_id || app.fdProviderId || app.provider_id || app.providerId || '').toLowerCase() === ShriramProviderId.toLowerCase());
1145
+ if (shriramComplete) ShriramSDKGlobalDataPayment = shriramComplete;
1146
+ }
1147
+ const fdListSelectedDataPayment = activeFD ? {
1148
+ id: appData.fd_id || appData.fdId || 'active-fd',
1149
+ providerId: providerIdFromApp,
1150
+ name: activeFD.name,
1151
+ accountNumber: appData.account_number || appData.accountNumber || '',
1152
+ roi: `${activeFD.returns}% p.a.`,
1153
+ tenure: activeFD.maturityDate || '-',
1154
+ amount: Number(activeFD.invested) || 0,
1155
+ maturityDate: activeFD.maturityDate || '-',
1156
+ status: 'active' as const,
1157
+ creditRating: appData.credit_rating || appData.creditRating || 'NA',
1158
+ companyName: activeFD.name,
1159
+ fdRate: `${activeFD.returns}% p.a.`,
1160
+ interestPayout: activeFD.interestPayout || 'Yearly',
1161
+ } : null;
1162
+ const masterDataPayment = ShriramMasterData;
1163
+ navigate('ExternalSDK', {
1164
+ sdkType: 'shriram',
1165
+ providerId: providerIdFromApp,
1166
+ fdListSelectedData: fdListSelectedDataPayment,
1167
+ masterData: masterDataPayment,
1168
+ onboardingIds: onboardingIdsFromApp || onboardingIds,
1169
+ initialRouteName: 'PaymentStatus',
1170
+ initialPaymentStatusParams: { status: 'pending', transactionId, fdData: fdDataParam },
1171
+ shriramSDKGlobalData: ShriramSDKGlobalDataPayment,
1172
+ } as any);
1173
+ return;
1174
+ }
1175
+ navigate('PaymentStatus', { status: 'pending', transactionId, fdData: fdDataParam } as any);
1176
+ return;
1177
+ }
1178
+ }
1179
+
1180
+ // Determine provider and navigate to appropriate SDK
1181
+ if (appData) {
1182
+ let providerIdFromApp = resolveProviderIdFromApp(appData, MahindraProviderId, ShriramProviderId);
1183
+ if (forcedSdkType === 'mahindra') providerIdFromApp = MahindraProviderId;
1184
+ if (forcedSdkType === 'shriram') providerIdFromApp = ShriramProviderId;
1185
+ const providerIdLower = (providerIdFromApp || '').toLowerCase();
1186
+ const isMahindra = providerIdLower === MahindraProviderId.toLowerCase();
1187
+ const isShriram = providerIdLower === ShriramProviderId.toLowerCase();
1188
+
1189
+ // Check for complete applications and create global data constants
1190
+ let ShriramSDKGlobalData: any = null;
1191
+ let MahindraSDKGlobalData: any = null;
1192
+
1193
+ if (customerApplications && !isCustomerApplicationsEmpty()) {
1194
+ let applicationsData: any[] = [];
1195
+ if (Array.isArray(customerApplications)) {
1196
+ applicationsData = customerApplications;
1197
+ } else if (customerApplications.data && Array.isArray(customerApplications.data)) {
1198
+ applicationsData = customerApplications.data;
1199
+ } else if (customerApplications.applications && Array.isArray(customerApplications.applications)) {
1200
+ applicationsData = customerApplications.applications;
1201
+ }
1202
+
1203
+ // Find complete applications
1204
+ const completeApplications = applicationsData.filter((app: any) => {
1205
+ const status = (app.wf_status || app.status || '').toString().toLowerCase();
1206
+ return status === 'completed' || status === 'complete';
1207
+ });
1208
+
1209
+ // Separate by provider
1210
+ completeApplications.forEach((app: any) => {
1211
+ const appProviderId = (app.fd_provider_id || app.fdProviderId || app.provider_id || app.providerId || '').toLowerCase();
1212
+ if (appProviderId === ShriramProviderId.toLowerCase()) {
1213
+ ShriramSDKGlobalData = app;
1214
+ } else if (appProviderId === MahindraProviderId.toLowerCase()) {
1215
+ MahindraSDKGlobalData = app;
1216
+ }
1217
+ });
1218
+ }
1219
+
1220
+ // Helper function to map workflow state to screen name
1221
+ const getScreenNameFromState = (state: number, isMahindraSDK: boolean, isShriramSDK: boolean): string => {
1222
+ if (isMahindraSDK) {
1223
+ if (state === MAHINDRA_WORKFLOW_STATES.KYC_SUCCESS) return 'KYCSuccess';
1224
+ if (state === MAHINDRA_WORKFLOW_STATES.KYC) return 'ReviewKYC';
1225
+ if (state === MAHINDRA_WORKFLOW_STATES.OCCUPATION) return 'Employee';
1226
+ if (state === MAHINDRA_WORKFLOW_STATES.NOMINEE) return 'NomineeDetail';
1227
+ if (state === MAHINDRA_WORKFLOW_STATES.BANK_DETAILS) return 'BankDetail';
1228
+ if (state === MAHINDRA_WORKFLOW_STATES.AADHAAR_VERIFICATION) return 'AadhaarVerification';
1229
+ if (state === MAHINDRA_WORKFLOW_STATES.PAYMENT) return 'PayNow';
1230
+ if (state === MAHINDRA_WORKFLOW_STATES.CREATE_FD) return 'FDCalculator';
1231
+ } else if (isShriramSDK) {
1232
+ if (state === SHRIRAM_WORKFLOW_STATES.KYC) return 'ReviewKYC';
1233
+ if (state === SHRIRAM_WORKFLOW_STATES.OCCUPATION) return 'Employee';
1234
+ if (state === SHRIRAM_WORKFLOW_STATES.NOMINEE) return 'NomineeDetail';
1235
+ if (state === SHRIRAM_WORKFLOW_STATES.BANK_DETAILS) return 'BankDetail';
1236
+ if (state === SHRIRAM_WORKFLOW_STATES.PAYMENT) return 'PayNow';
1237
+ // State 411 (CREATE_FD): user has completed up to FD creation; next step is payment → open PayNow
1238
+ if (state === SHRIRAM_WORKFLOW_STATES.CREATE_FD) return 'PayNow';
1239
+ }
1240
+ // Default fallback
1241
+ return 'FDCalculator';
1242
+ };
1243
+
1244
+ // Align pending flow with new-FD reliability: fetch provider-scoped master data right before navigation.
1245
+ let providerMasterData = isMahindra ? MahindraMasterData : isShriram ? ShriramMasterData : undefined;
1246
+ try {
1247
+ if (isMahindra) {
1248
+ const refreshed = await refetchMahindraMasterData();
1249
+ providerMasterData = refreshed?.data ?? providerMasterData;
1250
+ } else if (isShriram) {
1251
+ const refreshed = await refetchShriramMasterData();
1252
+ providerMasterData = refreshed?.data ?? providerMasterData;
1253
+ }
1254
+ } catch {
1255
+ // keep previously cached provider-specific master data
1256
+ }
1257
+
1258
+ const masterDataToPass = providerMasterData || (masterData || masterDataContext);
1259
+
1260
+ // Get onboardingIds - use the one from appData if available, otherwise from Redux store
1261
+ const onboardingIdsToPass = onboardingIdsFromApp || onboardingIds;
1262
+
1263
+ // Get fdListSelectedData (already set in Redux, but we need it for navigation)
1264
+ const fdListSelectedDataToPass = activeFD ? {
1265
+ id: appData.fd_id || appData.fdId || 'active-fd',
1266
+ providerId: providerIdFromApp,
1267
+ name: activeFD.name,
1268
+ accountNumber: appData.account_number || appData.accountNumber || '',
1269
+ roi: `${activeFD.returns}% p.a.`,
1270
+ tenure: activeFD.tenure_in_months
1271
+ ? `${activeFD.tenure_in_months} Months`
1272
+ : '-',
1273
+ amount: Number(activeFD.invested) || 0,
1274
+ maturityDate: activeFD.maturityDate || '-',
1275
+ status: 'active' as const,
1276
+ creditRating: appData.credit_rating || appData.creditRating || selectedFD?.creditRating || 'NA',
1277
+ companyName: activeFD.name,
1278
+ fdRate: `${activeFD.returns}% p.a.`,
1279
+ interestPayout: activeFD.interestPayout || '',
1280
+ } : null;
1281
+
1282
+ // Navigate to appropriate SDK if provider is detected
1283
+ if (isMahindra || isShriram) {
1284
+ const screenName = getScreenNameFromState(currentState, isMahindra, isShriram);
1285
+ const sdkType = isMahindra ? 'mahindra' : 'shriram';
1286
+ if (!initializeExternalSDKFromFDList(sdkType)) return;
1287
+ // #region agent log
1288
+ fetch('http://127.0.0.1:7653/ingest/94b18a39-caf3-48fd-98f5-bf1838d9491a',{method:'POST',headers:{'Content-Type':'application/json','X-Debug-Session-Id':'04da7c'},body:JSON.stringify({sessionId:'04da7c',runId:'pending-masterdata-trace',hypothesisId:'H2',location:'fdsdk/screens/FDList.tsx:1315',message:'Pending continue pre-navigation payload',data:{forcedSdkType:forcedSdkType||null,resolvedSdkType:sdkType,providerIdFromApp:providerIdFromApp||null,onboardingProviderId:onboardingIdsToPass?.providerId||null,fdListSelectedProviderId:fdListSelectedDataToPass?.providerId||null,usingMahindraMasterData:!!(sdkType==='mahindra'&&masterDataToPass),usingShriramMasterData:!!(sdkType==='shriram'&&masterDataToPass),masterDataHasDataKey:!!masterDataToPass?.data},timestamp:Date.now()})}).catch(()=>{});
1289
+ // #endregion agent log
1290
+
1291
+ navigate('ExternalSDK', {
1292
+ sdkType,
1293
+ providerId: providerIdFromApp,
1294
+ fdListSelectedData: fdListSelectedDataToPass,
1295
+ masterData: masterDataToPass,
1296
+ onboardingIds: onboardingIdsToPass,
1297
+ initialRouteName: screenName, // Pass the screen name to navigate to
1298
+ shriramSDKGlobalData: ShriramSDKGlobalData, // Pass complete Shriram application data
1299
+ mahindraSDKGlobalData: MahindraSDKGlobalData, // Pass complete Mahindra application data
1300
+ // Let Mahindra SDK fetch details itself when launched from host (Mahindra only)
1301
+ ...(isMahindra ? { forceFetchCustomerDetails: true } : {}),
1302
+ });
1303
+ return;
1304
+ }
1305
+ }
1306
+
1307
+ // Fallback: Use workflow navigation based on current state (non-payment states or no transactionId)
1308
+ if (currentState && Object.values(WORKFLOW_STATES).includes(currentState)) {
1309
+ handleWorkflowNavigation({
1310
+ workflowState: currentState,
1311
+ investmentData: undefined,
1312
+ customerData: undefined,
1313
+ appData: undefined,
1314
+ fdData: undefined,
1315
+ completedApplications: [],
1316
+ transactionId,
1317
+ });
1318
+ } else {
1319
+ navigate('Employee');
1320
+ }
1321
+ };
1322
+
1323
+ // Derive identifiers for workflow termination from applications API
1324
+ // Derive Active FD card data from customer applications response
1325
+ const activeFD = React.useMemo(() => {
1326
+ try {
1327
+ if (!customerApplications || isCustomerApplicationsEmpty()) {
1328
+ return {
1329
+ name: 'Mahindra Finance Ltd',
1330
+ invested: 0,
1331
+ value: 0,
1332
+ returns: 0,
1333
+ maturityDate: '--',
1334
+ tenure_in_months: 0,
1335
+ interestPayout: 'Yearly',
1336
+ logoType: 'mahindra' as const,
1337
+ };
1338
+ }
1339
+
1340
+ // Normalize list
1341
+ let applicationsData: any[] = [];
1342
+ if (Array.isArray(customerApplications)) {
1343
+ applicationsData = customerApplications;
1344
+ } else if (customerApplications.data && Array.isArray(customerApplications.data)) {
1345
+ applicationsData = customerApplications.data;
1346
+ } else if (customerApplications.applications && Array.isArray(customerApplications.applications)) {
1347
+ applicationsData = customerApplications.applications;
1348
+ }
1349
+
1350
+ if (applicationsData.length === 0) {
1351
+ return {
1352
+ name: 'Mahindra Finance Ltd',
1353
+ invested: 0,
1354
+ value: 0,
1355
+ returns: 0,
1356
+ maturityDate: '--',
1357
+ tenure_in_months: 0,
1358
+ interestPayout: 'Yearly',
1359
+ logoType: 'mahindra' as const,
1360
+ };
1361
+ }
1362
+
1363
+ // Pick an active application if available, otherwise first
1364
+ const activeApp = applicationsData.find((a: any) => {
1365
+ const s = (a.wf_status || a.status || '').toString();
1366
+ return s === 'Active';
1367
+ }) || applicationsData[0];
1368
+
1369
+ const invested = Number(
1370
+ activeApp.investment_amount ?? activeApp.amount ?? activeApp.investmentAmount ?? 0
1371
+ );
1372
+ const rate = Number(
1373
+ activeApp.interest_rate ?? activeApp.interestRate ?? 0
1374
+ );
1375
+
1376
+ // Prefer a provided current value, then maturity amount
1377
+ const value = Number(
1378
+ activeApp.current_value ?? activeApp.currentValue ?? activeApp.maturity_amount ?? activeApp.maturityAmount ?? 0
1379
+ );
1380
+
1381
+ const returns = rate || 0; // display interest rate as returns %
1382
+
1383
+ // Format maturity date to 'Mon YYYY'
1384
+ const maturityDateStr: string = activeApp.maturity_date || activeApp.maturityDate || '';
1385
+ let maturityDate = '--';
1386
+ if (maturityDateStr) {
1387
+ const d = new Date(maturityDateStr);
1388
+ if (!isNaN(d.getTime())) {
1389
+ const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
1390
+ maturityDate = `${months[d.getMonth()]} ${d.getFullYear()}`;
1391
+ }
1392
+ }
1393
+
1394
+ // Get interest payout term from activeApp
1395
+ const interestPayout = activeApp.interest_payout_term || activeApp.interestPayoutTerm || activeApp.payout_term || 'Yearly';
1396
+
1397
+ const providerName = activeApp.fd_provider_name || activeApp.providerName || 'Mahindra Finance Ltd';
1398
+ const logoType = getLogoTypeForFD({ name: providerName, providerId: activeApp.fd_provider_id || activeApp.fdProviderId || '' });
1399
+
1400
+ return {
1401
+ name: providerName,
1402
+ invested,
1403
+ value,
1404
+ returns,
1405
+ maturityDate,
1406
+ tenure_in_months: activeApp.tenure_in_months ?? 0,
1407
+ interestPayout,
1408
+ logoType,
1409
+ };
1410
+ } catch (e) {
1411
+ return {
1412
+ name: 'Mahindra Finance Ltd',
1413
+ invested: 0,
1414
+ value: 0,
1415
+ returns: 0,
1416
+ maturityDate: '--',
1417
+ tenure_in_months: 0,
1418
+ interestPayout: 'Yearly',
1419
+ logoType: 'mahindra' as const,
1420
+ };
1421
+ }
1422
+ }, [customerApplications]);
1423
+
1424
+ // All pending/active FDs (one per application with Active or pending/in_progress) – for multiple providers
1425
+ const activeFDs = React.useMemo(() => {
1426
+ try {
1427
+ if (!customerApplications || isCustomerApplicationsEmpty()) return [];
1428
+ let applicationsData: any[] = [];
1429
+ if (Array.isArray(customerApplications)) {
1430
+ applicationsData = customerApplications;
1431
+ } else if (customerApplications.data && Array.isArray(customerApplications.data)) {
1432
+ applicationsData = customerApplications.data;
1433
+ } else if (customerApplications.applications && Array.isArray(customerApplications.applications)) {
1434
+ applicationsData = customerApplications.applications;
1435
+ }
1436
+ const pendingActive = applicationsData.filter((a: any) => {
1437
+ const s = (a.wf_status || a.status || '').toString().toLowerCase();
1438
+ return PENDING_ACTIVE_STATUSES.includes(s);
1439
+ });
1440
+ return pendingActive.map((app: any) => ({
1441
+ ...appToPendingFDData(app),
1442
+ app,
1443
+ }));
1444
+ } catch (e) {
1445
+ return [];
1446
+ }
1447
+ }, [customerApplications]);
1448
+
1449
+ // Normalized applications list for provider-scoped lookups
1450
+ const applicationsDataNormalized = React.useMemo(() => {
1451
+ if (!customerApplications || isCustomerApplicationsEmpty()) return [];
1452
+ if (Array.isArray(customerApplications)) return customerApplications;
1453
+ if (customerApplications.data && Array.isArray(customerApplications.data)) return customerApplications.data;
1454
+ if (customerApplications.applications && Array.isArray(customerApplications.applications)) return customerApplications.applications;
1455
+ return [];
1456
+ }, [customerApplications]);
1457
+
1458
+ // Helper function to check if there's any active/pending FD
1459
+ const hasActiveFD = React.useMemo(() => activeFDs.length > 0, [activeFDs]);
1460
+
1461
+ // Pending FD data for bottom sheet: from the app we're showing (provider they tapped), or fallback to first active
1462
+ const pendingFDDataForSheet = React.useMemo(() => {
1463
+ if (pendingContextApp) return appToPendingFDData(pendingContextApp);
1464
+ if (activeFDs.length > 0) return activeFDs[0]; // fallback: first card data (no app ref needed for display)
1465
+ return activeFD;
1466
+ }, [pendingContextApp, activeFDs, activeFD]);
1467
+
1468
+ // Build FD options list from interest-rate API (show all FDs)
1469
+ const fdList = React.useMemo(() => {
1470
+ if (interestRates && (interestRates.data?.sdrScheme?.length > 0 || interestRates.data?.fdrScheme?.length > 0)) {
1471
+ const sdr = Array.isArray(interestRates.data?.sdrScheme) ? interestRates.data.sdrScheme : [];
1472
+ const fdr = Array.isArray(interestRates.data?.fdrScheme) ? interestRates.data.fdrScheme : [];
1473
+ const combined = [...sdr, ...fdr];
1474
+
1475
+ // Building FD options from rates
1476
+
1477
+ return combined.map((rate: any, index: number) => ({
1478
+ id: `${rate.providerId || 'provider'}-${rate.perdMonth || index}`,
1479
+ providerId: rate.providerId || `provider-${index}`,
1480
+ name: rate.providerName || '', // Use actual provider name from API
1481
+ accountNumber: `FD${String(index + 1).padStart(9, '0')}`,
1482
+ roi: `${Number(rate.rate ?? rate.interestRate ?? 0).toFixed(2)}% p.a.`,
1483
+ tenure: `${rate.perdMonth} Months`,
1484
+ amount: 5000,
1485
+ maturityDate: '2025-12-31',
1486
+ status: 'active' as const,
1487
+ creditRating: rate.creditRating || 'AA+',
1488
+ interestPayout: rate.interestPayout || rate.interest_payout_term || rate.payout_term || 'Yearly'
1489
+ }));
1490
+ }
1491
+
1492
+ // If rates are not available yet, show empty list
1493
+ return [];
1494
+ }, [interestRates]);
1495
+
1496
+
1497
+ // Get filtered FD list based on active tab
1498
+ const filteredFDList = React.useMemo(() => {
1499
+ if (!fdList || fdList.length === 0) return [];
1500
+
1501
+ // Always create a new filtered array
1502
+ const filtered = filterFDsByTenure([...fdList], activeTab); // spread to ensure new array
1503
+
1504
+ return filtered;
1505
+ }, [fdList, activeTab]);
1506
+
1507
+
1508
+ const tabs = [FD_STRINGS.ALL_FDS_TAB, FD_STRINGS.LESS_THAN_1Y_TAB, FD_STRINGS.LESS_THAN_1_3Y_TAB, FD_STRINGS.LESS_THAN_3_5Y_TAB, FD_STRINGS.GREATER_THAN_5Y_TAB];
1509
+ const poweredByLogo = (getAppData()?.poweredByLogo || '').trim();
1510
+
1511
+ if (!isAllDataReady) {
1512
+ return (
1513
+ <SafeAreaWrapper
1514
+ style={customStyles.container}
1515
+ includeTop={false}
1516
+ statusBarColor={colors.headerBg}
1517
+ statusBarStyle="light-content"
1518
+ >
1519
+ <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
1520
+ <ActivityIndicator size="large" color={colors.primary} />
1521
+ <Text style={{ marginTop: 12, color: colors.textSecondary }}>{FD_STRINGS.LOADING_FD_DATA}</Text>
1522
+ </View>
1523
+ </SafeAreaWrapper>
1524
+ );
1525
+ }
1526
+
1527
+ return (
1528
+ <SafeAreaWrapper
1529
+ style={customStyles.container}
1530
+ includeTop={false}
1531
+ statusBarColor={colors.headerBg}
1532
+ statusBarStyle="light-content"
1533
+ >
1534
+ {/* Custom FDList Header - same structure as Shriram FDList */}
1535
+ <View style={styles.customHeader}>
1536
+ <TouchableOpacity
1537
+ style={styles.backButton}
1538
+ onPress={onGoBack}
1539
+ activeOpacity={0.7}
1540
+ >
1541
+ <Image
1542
+ source={{ uri: base64Images.backArrow }}
1543
+ style={styles.backIcon}
1544
+ resizeMode="contain"
1545
+ />
1546
+ </TouchableOpacity>
1547
+
1548
+ <View style={styles.headerContent}>
1549
+ <Text style={styles.headerTitle}>{FD_STRINGS.CORPORATE_FDS_TITLE}</Text>
1550
+ {poweredByLogo ? (
1551
+ <View style={styles.poweredByContainer}>
1552
+ <Text style={styles.poweredByText}>Powered by</Text>
1553
+ <Image
1554
+ source={{ uri: poweredByLogo }}
1555
+ style={styles.simplifyLogo}
1556
+ resizeMode="contain"
1557
+ />
1558
+ </View>
1559
+ ) : null}
1560
+ </View>
1561
+ </View>
1562
+
1563
+ <ScrollView style={styles.content}>
1564
+
1565
+ {/* Active FDs Section - One card per pending/active FD (e.g. one Mahindra, one Shriram) */}
1566
+ {hasActiveFD && (
1567
+ <View style={styles.section}>
1568
+ <Text style={styles.sectionTitle}>{FD_STRINGS.ACTIVE_FDS_SECTION}</Text>
1569
+ {activeFDs.map((item, index) => (
1570
+ <TouchableOpacity
1571
+ key={item.app?.application_id || item.app?.applicationId || index}
1572
+ activeOpacity={0.8}
1573
+ onPress={() => handlePendingFDContinue(item.app, item.logoType)}
1574
+ style={index > 0 ? styles.activeFDCardSpacer : undefined}
1575
+ >
1576
+ <ActiveFDCard
1577
+ name={item.name}
1578
+ invested={item.invested}
1579
+ value={item.value}
1580
+ returns={item.returns}
1581
+ maturityDate={item.maturityDate}
1582
+ logoType={item.logoType}
1583
+ />
1584
+ </TouchableOpacity>
1585
+ ))}
1586
+ </View>
1587
+ )}
1588
+
1589
+ {/* Tabs */}
1590
+ <View style={styles.tabsContainer}>
1591
+ <View style={styles.tabsRow}>
1592
+ {tabs.map((tab) => (
1593
+ <TouchableOpacity
1594
+ key={tab}
1595
+ style={[
1596
+ styles.tab,
1597
+ activeTab === tab && styles.activeTab
1598
+ ]}
1599
+ onPress={() => {
1600
+ // Other tabs filter the data
1601
+ setActiveTab(tab);
1602
+ }}
1603
+ >
1604
+ <Text style={[
1605
+ styles.tabText,
1606
+ activeTab === tab && styles.activeTabText
1607
+ ]}>
1608
+ {tab}
1609
+ </Text>
1610
+ </TouchableOpacity>
1611
+ ))}
1612
+ </View>
1613
+ </View>
1614
+
1615
+ {/* Filter Status removed as requested */}
1616
+
1617
+ {/* FD List */}
1618
+ <View style={styles.fdOptionsContainer}>
1619
+ {filteredFDList.length > 0 ? (
1620
+ filteredFDList.map((fd: FDItem) => (
1621
+ <FDCard
1622
+ key={fd.id}
1623
+ id={fd.id}
1624
+ name={fd.name}
1625
+ accountNumber={fd.accountNumber}
1626
+ roi={fd.roi}
1627
+ tenure={fd.tenure}
1628
+ amount={fd.amount}
1629
+ maturityDate={fd.maturityDate}
1630
+ status={fd.status as 'active' | 'matured' | 'pending'}
1631
+ creditRating={fd.creditRating}
1632
+ logoType={getLogoTypeForFD(fd)}
1633
+ onPress={(fdId) => {
1634
+
1635
+
1636
+ // Log the selection
1637
+ onSelectFD?.(fdId);
1638
+
1639
+ // Store the selected FD data locally
1640
+ setSelectedFD(fd);
1641
+
1642
+ // Store the selected FD data in Redux for PayNow screen
1643
+ const fdListSelectedData = {
1644
+ id: fd.id,
1645
+ providerId: fd.providerId,
1646
+ name: fd.name,
1647
+ accountNumber: fd.accountNumber,
1648
+ roi: fd.roi,
1649
+ tenure: fd.tenure,
1650
+ amount: fd.amount,
1651
+ maturityDate: fd.maturityDate,
1652
+ status: fd.status as 'active' | 'matured' | 'pending',
1653
+ creditRating: fd.creditRating,
1654
+ // Additional fields for PayNow display
1655
+ companyName: fd.name,
1656
+ fdRate: fd.roi,
1657
+ interestPayout: fd.interestPayout || 'Yearly', // Use FD's interest payout or default
1658
+ };
1659
+
1660
+ dispatch(setFDListSelected(fdListSelectedData));
1661
+
1662
+ // 1st Place: Set onboardingIds when customer taps on any FD from FDList
1663
+ // Start with existing onboardingIds, but prefer the freshly computed ones for this tap.
1664
+ let onboardingIdsForNav: any = onboardingIds;
1665
+ try {
1666
+ let idsToSet: any = { providerId: fd.providerId };
1667
+
1668
+ // If we already have customer applications, enrich onboardingIds
1669
+ try {
1670
+ if (customerApplications && !isCustomerApplicationsEmpty()) {
1671
+ // Normalize list
1672
+ let applicationsData: any[] = [];
1673
+ if (Array.isArray(customerApplications)) {
1674
+ applicationsData = customerApplications;
1675
+ } else if (customerApplications.data && Array.isArray(customerApplications.data)) {
1676
+ applicationsData = customerApplications.data;
1677
+ } else if (customerApplications.applications && Array.isArray(customerApplications.applications)) {
1678
+ applicationsData = customerApplications.applications;
1679
+ }
1680
+
1681
+ if (applicationsData.length > 0) {
1682
+ // Prefer applications that belong to the same provider as the tapped FD
1683
+ const fdProviderIdLower = (fd.providerId || '').toLowerCase();
1684
+ const appsForProvider = applicationsData.filter((a: any) => {
1685
+ const appProviderId =
1686
+ (a.fd_provider_id ||
1687
+ a.fdProviderId ||
1688
+ a.provider_id ||
1689
+ a.providerId ||
1690
+ '').toLowerCase();
1691
+ return appProviderId === fdProviderIdLower;
1692
+ });
1693
+
1694
+ const scopedApplications =
1695
+ appsForProvider.length > 0 ? appsForProvider : applicationsData;
1696
+
1697
+ // Within the scoped list, still prefer the Active application
1698
+ const appData = scopedApplications.find((a: any) => {
1699
+ const s = (a.wf_status || a.status || '').toString();
1700
+ return s === 'Active';
1701
+ }) || scopedApplications[0];
1702
+
1703
+ idsToSet = {
1704
+ workflowInstanceId: appData.workflow_instance_id || appData.workflowInstanceId,
1705
+ applicationId: appData.application_id || appData.applicationId,
1706
+ entityid: appData.entity_id || appData.entityId,
1707
+ customerId: appData.customer_id || appData.customerId,
1708
+ fdId: appData.fd_id || appData.fdId,
1709
+ currentState: appData.current_state || appData.currentState,
1710
+ currentTask: appData.current_task || appData.currentTask,
1711
+ // Check both snake_case (fd_provider_id) and camelCase (fdProviderId) variants
1712
+ providerId: fd.providerId,
1713
+ wfStatus: appData.wf_status || appData.wfStatus,
1714
+ };
1715
+ }
1716
+ }
1717
+ } catch (innerError) {
1718
+ // If enrichment fails, fall back to providerId only
1719
+ }
1720
+
1721
+ dispatch(setOnboardingIds(idsToSet));
1722
+ onboardingIdsForNav = idsToSet;
1723
+ } catch (e) {
1724
+ }
1725
+
1726
+ // Check if this provider has a pending/active FD – show sheet only for that provider
1727
+ const pendingAppForProvider = getPendingOrActiveAppForProvider(applicationsDataNormalized, fd.providerId || '');
1728
+ if (pendingAppForProvider) {
1729
+ setPendingContextApp(pendingAppForProvider);
1730
+ setShowPendingFDBottomSheet(true);
1731
+ } else {
1732
+ // If no active FD, navigate to appropriate SDK FD calculator
1733
+ const fdProviderId = (fd.providerId || '').toLowerCase();
1734
+ const isMahindra = fdProviderId === MahindraProviderId.toLowerCase();
1735
+ const isShriram = fdProviderId === ShriramProviderId.toLowerCase();
1736
+
1737
+ // Check for complete applications and create global data constants
1738
+ let ShriramSDKGlobalData: any = null;
1739
+ let MahindraSDKGlobalData: any = null;
1740
+
1741
+ if (customerApplications && !isCustomerApplicationsEmpty()) {
1742
+ let applicationsData: any[] = [];
1743
+ if (Array.isArray(customerApplications)) {
1744
+ applicationsData = customerApplications;
1745
+ } else if (customerApplications.data && Array.isArray(customerApplications.data)) {
1746
+ applicationsData = customerApplications.data;
1747
+ } else if (customerApplications.applications && Array.isArray(customerApplications.applications)) {
1748
+ applicationsData = customerApplications.applications;
1749
+ }
1750
+
1751
+ // Find complete applications
1752
+ const completeApplications = applicationsData.filter((app: any) => {
1753
+ const status = (app.wf_status || app.status || '').toString().toLowerCase();
1754
+ return status === 'completed' || status === 'complete';
1755
+ });
1756
+
1757
+ // Separate by provider
1758
+ completeApplications.forEach((app: any) => {
1759
+ const appProviderId = (app.fd_provider_id || app.fdProviderId || app.provider_id || app.providerId || '').toLowerCase();
1760
+ if (appProviderId === ShriramProviderId.toLowerCase()) {
1761
+ ShriramSDKGlobalData = app;
1762
+ } else if (appProviderId === MahindraProviderId.toLowerCase()) {
1763
+ MahindraSDKGlobalData = app;
1764
+ }
1765
+ });
1766
+ }
1767
+
1768
+ if (isShriram) {
1769
+ navigate('ExternalSDK', {
1770
+ sdkType: 'shriram',
1771
+ providerId: fd.providerId,
1772
+ fdListSelectedData: fdListSelectedData,
1773
+ masterData: ShriramMasterData,
1774
+ // Use the freshest onboardingIds (from this tap if available)
1775
+ onboardingIds: onboardingIdsForNav,
1776
+ shriramSDKGlobalData: ShriramSDKGlobalData, // Pass complete Shriram application data
1777
+ mahindraSDKGlobalData: MahindraSDKGlobalData, // Pass complete Mahindra application data
1778
+ });
1779
+ } else if (isMahindra) {
1780
+ // Navigate to Mahindra SDK FD calculator
1781
+ navigate('ExternalSDK', {
1782
+ sdkType: 'mahindra',
1783
+ providerId: fd.providerId,
1784
+ fdListSelectedData: fdListSelectedData,
1785
+ masterData: MahindraMasterData,
1786
+ // Use the freshest onboardingIds (from this tap if available)
1787
+ onboardingIds: onboardingIdsForNav,
1788
+ shriramSDKGlobalData: ShriramSDKGlobalData, // Pass complete Shriram application data
1789
+ mahindraSDKGlobalData: MahindraSDKGlobalData, // Pass complete Mahindra application data
1790
+ });
1791
+ } else {
1792
+ // Fallback to default FD calculator
1793
+ onNavigateToFDCalculator?.(fd, masterData);
1794
+ }
1795
+ }
1796
+ }}
1797
+ // onPress={(fdId) => {
1798
+ // const fdProviderId = (fd.providerId || '').toLowerCase();
1799
+ // const isMahindra = fdProviderId === MahindraProviderId.toLowerCase();
1800
+ // const isShriram = fdProviderId === ShriramProviderId.toLowerCase();
1801
+
1802
+ // // Construct FDListSelectedData object
1803
+ // const fdListSelectedData = {
1804
+ // id: fd.id,
1805
+ // providerId: fd.providerId,
1806
+ // name: fd.name,
1807
+ // accountNumber: fd.accountNumber,
1808
+ // roi: fd.roi,
1809
+ // tenure: fd.tenure,
1810
+ // amount: fd.amount,
1811
+ // maturityDate: fd.maturityDate,
1812
+ // status: fd.status as 'active' | 'matured' | 'pending',
1813
+ // creditRating: fd.creditRating,
1814
+ // // Additional fields for PayNow display
1815
+ // companyName: fd.name,
1816
+ // fdRate: fd.roi,
1817
+ // interestPayout: fd.interestPayout || 'Yearly', // Use FD's interest payout or default
1818
+ // };
1819
+
1820
+ // console.log('[FDList][onPress] ===== FD Tapped - Data Logs =====');
1821
+ // console.log('[FDList][onPress] FDListSelectedData:', fdListSelectedData);
1822
+ // console.log('[FDList][onPress] OnboardingIds:', onboardingIds);
1823
+
1824
+ // if (isMahindra) {
1825
+ // console.log('[FDList][onPress] Mahindra MasterData:', MahindraMasterData);
1826
+ // } else if (isShriram) {
1827
+ // console.log('[FDList][onPress] Shriram MasterData:', ShriramMasterData);
1828
+ // } else {
1829
+ // console.log('[FDList][onPress] MasterData (fallback):', masterData || masterDataContext);
1830
+ // }
1831
+
1832
+ // console.log('[FDList][onPress] ====================================');
1833
+ // }}
1834
+ customStyles={customStyles}
1835
+ />
1836
+ ))
1837
+ ) : (
1838
+ <View style={styles.noDataContainer}>
1839
+ <Text style={styles.noDataText}>
1840
+ {isLoadingRates || isLoadingApplications ? FD_STRINGS.LOADING_FD_DATA :
1841
+ interestRatesError || customerApplicationsError ? FD_STRINGS.FAILED_TO_LOAD_FD_DATA :
1842
+ fdList.length === 0 ? (!isCustomerApplicationsEmpty() ? FD_STRINGS.NO_CUSTOMER_FDS_FOUND : FD_STRINGS.NO_FD_OPTIONS_AVAILABLE) :
1843
+ `${FD_STRINGS.NO_FDS_FOUND_FOR_TENURE} "${activeTab}" tenure range.`}
1844
+ </Text>
1845
+
1846
+ </View>
1847
+ )}
1848
+ </View>
1849
+ </ScrollView>
1850
+
1851
+ {/* Pending FD Bottom Sheet - shows pending/active FD for the provider they tapped */}
1852
+ <PendingFDBottomSheet
1853
+ visible={showPendingFDBottomSheet}
1854
+ onClose={() => {
1855
+ setShowPendingFDBottomSheet(false);
1856
+ setPendingContextApp(null);
1857
+ }}
1858
+ pendingFDData={pendingFDDataForSheet}
1859
+ isBookingNewLoading={isTerminatingWorkflow}
1860
+ onContinue={async () => {
1861
+ setShowPendingFDBottomSheet(false);
1862
+ const forcedFromSelected = getSdkTypeFromProviderId(
1863
+ selectedFD?.providerId || '',
1864
+ MahindraProviderId,
1865
+ ShriramProviderId
1866
+ ) || undefined;
1867
+ await handlePendingFDContinue(undefined, forcedFromSelected);
1868
+ }}
1869
+ onBookNew={async () => {
1870
+ try {
1871
+ // Terminate only the workflow for the provider we're showing (the one they tapped)
1872
+ const appToTerminate = pendingContextApp || (activeFDs.length > 0 ? activeFDs[0].app : null);
1873
+ const providerIdForTerminate = appToTerminate
1874
+ ? (appToTerminate.fd_provider_id || appToTerminate.fdProviderId || appToTerminate.provider_id || appToTerminate.providerId)
1875
+ : (selectedFD?.providerId || inferredProviderId);
1876
+ const requestParams = {
1877
+ providerId: providerIdForTerminate,
1878
+ workflowInstanceId: appToTerminate?.workflow_instance_id || appToTerminate?.workflowInstanceId || terminateIdentifiers.workflowInstanceId || 'wf-instance-unknown',
1879
+ entityId: appToTerminate?.entity_id || appToTerminate?.entityId || terminateIdentifiers.entityId || 'entity-unknown',
1880
+ applicationId: appToTerminate?.application_id || appToTerminate?.applicationId || terminateIdentifiers.applicationId || 'application-unknown',
1881
+ } as const;
1882
+
1883
+ // Call workflow terminate API (only for this provider's workflow)
1884
+ await terminateWorkflow(requestParams);
1885
+
1886
+ // 2nd Place: Set onboardingIds when user taps "I want to create new"
1887
+ try {
1888
+ const providerIdToSet = selectedFD?.providerId || inferredProviderId || getProviderIdFromInterestRates();
1889
+ if (providerIdToSet) {
1890
+ const idsToSet = { providerId: providerIdToSet };
1891
+ dispatch(setOnboardingIds(idsToSet));
1892
+ } else {
1893
+ }
1894
+ } catch (e) {
1895
+ }
1896
+
1897
+ // Dismiss bottom sheet
1898
+ setShowPendingFDBottomSheet(false);
1899
+
1900
+ // Navigate to provider-specific SDK (Mahindra/Shriram) just like new FD tap
1901
+ const providerIdToUse = (selectedFD?.providerId || inferredProviderId || getProviderIdFromInterestRates() || '').toLowerCase();
1902
+ const isMahindra = providerIdToUse === MahindraProviderId.toLowerCase();
1903
+ const isShriram = providerIdToUse === ShriramProviderId.toLowerCase();
1904
+
1905
+ // Build fdListSelectedData from selectedFD when available (best effort)
1906
+ const fdListSelectedDataToPass = selectedFD ? {
1907
+ id: selectedFD.id || 'new-fd',
1908
+ providerId: selectedFD.providerId,
1909
+ name: selectedFD.name,
1910
+ accountNumber: selectedFD.accountNumber,
1911
+ roi: selectedFD.roi,
1912
+ tenure: selectedFD.tenure,
1913
+ amount: selectedFD.amount,
1914
+ maturityDate: selectedFD.maturityDate,
1915
+ status: selectedFD.status as 'active' | 'matured' | 'pending' | undefined,
1916
+ creditRating: selectedFD.creditRating,
1917
+ companyName: selectedFD.name,
1918
+ fdRate: selectedFD.roi,
1919
+ interestPayout: selectedFD.interestPayout || 'Yearly',
1920
+ } : undefined;
1921
+
1922
+ const masterDataToPass = isMahindra
1923
+ ? MahindraMasterData
1924
+ : isShriram
1925
+ ? ShriramMasterData
1926
+ : (masterData || masterDataContext);
1927
+
1928
+ // Recompute complete application global data constants (same as tap flow)
1929
+ let ShriramSDKGlobalData: any = null;
1930
+ let MahindraSDKGlobalData: any = null;
1931
+ try {
1932
+ if (customerApplications && !isCustomerApplicationsEmpty()) {
1933
+ let applicationsData: any[] = [];
1934
+ if (Array.isArray(customerApplications)) {
1935
+ applicationsData = customerApplications;
1936
+ } else if (customerApplications.data && Array.isArray(customerApplications.data)) {
1937
+ applicationsData = customerApplications.data;
1938
+ } else if (customerApplications.applications && Array.isArray(customerApplications.applications)) {
1939
+ applicationsData = customerApplications.applications;
1940
+ }
1941
+
1942
+ const completeApplications = applicationsData.filter((app: any) => {
1943
+ const status = (app.wf_status || app.status || '').toString().toLowerCase();
1944
+ return status === 'completed' || status === 'complete';
1945
+ });
1946
+
1947
+ completeApplications.forEach((app: any) => {
1948
+ const appProviderId = (app.fd_provider_id || app.fdProviderId || app.provider_id || app.providerId || '').toLowerCase();
1949
+ if (appProviderId === ShriramProviderId.toLowerCase()) {
1950
+ ShriramSDKGlobalData = app;
1951
+ } else if (appProviderId === MahindraProviderId.toLowerCase()) {
1952
+ MahindraSDKGlobalData = app;
1953
+ }
1954
+ });
1955
+ }
1956
+ } catch (e) {
1957
+ // ignore errors
1958
+ }
1959
+
1960
+ if (isMahindra || isShriram) {
1961
+ navigate('ExternalSDK', {
1962
+ sdkType: isMahindra ? 'mahindra' : 'shriram',
1963
+ providerId: selectedFD?.providerId || inferredProviderId,
1964
+ fdListSelectedData: fdListSelectedDataToPass,
1965
+ masterData: masterDataToPass,
1966
+ onboardingIds: onboardingIds,
1967
+ shriramSDKGlobalData: ShriramSDKGlobalData,
1968
+ mahindraSDKGlobalData: MahindraSDKGlobalData,
1969
+ });
1970
+ } else {
1971
+ // Fallback to host FD calculator
1972
+ onNavigateToFDCalculator?.();
1973
+ }
1974
+
1975
+ } catch (error) {
1976
+ // Handle error silently
1977
+
1978
+ // Still dismiss bottom sheet and navigate even if API fails
1979
+ // setShowPendingFDBottomSheet(false);
1980
+ // onNavigateToFDCalculator?.();
1981
+
1982
+ // Show error alert
1983
+ Alert.alert(
1984
+ 'Warning',
1985
+ 'Failed to terminate existing workflow, but continuing with new FD booking.',
1986
+ [{ text: 'OK' }]
1987
+ );
1988
+ }
1989
+ }}
1990
+ />
1991
+
1992
+ {/* Full-screen loader while Mahindra customer/application/details are being fetched
1993
+ (for Mahindra-only pending flows). */}
1994
+ {isLoadingMahindraCustomerDetails && (
1995
+ <View style={styles.loadingOverlay} pointerEvents="auto">
1996
+ <ActivityIndicator size="large" color={colors.primary} />
1997
+ </View>
1998
+ )}
1999
+ </SafeAreaWrapper>
2000
+ );
2001
+ };
2002
+
2003
+ const createStyles = (colors: ColorScheme, typography: any, spacing: any, themeName: ThemeName) => StyleSheet.create({
2004
+ container: {
2005
+ flex: 1,
2006
+ backgroundColor: colors.surface,
2007
+ },
2008
+ customHeader: {
2009
+ backgroundColor: colors.headerBg,
2010
+ paddingTop: Platform.OS === 'ios' ? 50 : 20,
2011
+ paddingBottom: spacing.md,
2012
+ paddingHorizontal: spacing.lg,
2013
+ flexDirection: 'row',
2014
+ alignItems: 'center',
2015
+ },
2016
+ backButton: {
2017
+ width: 40,
2018
+ height: 40,
2019
+ alignItems: 'center',
2020
+ justifyContent: 'center',
2021
+ },
2022
+ backIcon: {
2023
+ width: 24,
2024
+ height: 24,
2025
+ tintColor: colors.headerText,
2026
+ },
2027
+ headerContent: {
2028
+ flex: 1,
2029
+ alignItems: 'flex-start',
2030
+ marginRight: 40, // Balance the back button width
2031
+ },
2032
+ headerTitle: {
2033
+ ...typography.styles.h3,
2034
+ color: colors.headerText,
2035
+ marginBottom: spacing.xs,
2036
+ },
2037
+ poweredByContainer: {
2038
+ flexDirection: 'row',
2039
+ alignItems: 'center',
2040
+ },
2041
+ poweredByText: {
2042
+ ...typography.styles.text10Regular,
2043
+ color: colors.headerText,
2044
+ opacity: 0.7,
2045
+ marginRight: 0,
2046
+ },
2047
+ simplifyLogo: {
2048
+ width: 70,
2049
+ height: 20,
2050
+ },
2051
+ content: {
2052
+ flex: 1,
2053
+ },
2054
+ section: {
2055
+ paddingHorizontal: spacing.lg,
2056
+ paddingTop: spacing.xl,
2057
+ },
2058
+ sectionTitle: {
2059
+ ...typography.styles.h3,
2060
+ color: colors.text,
2061
+ marginBottom: spacing.lg,
2062
+ },
2063
+ activeFDCardSpacer: {
2064
+ marginTop: spacing.md,
2065
+ },
2066
+ tabsContainer: {
2067
+ paddingHorizontal: spacing.lg,
2068
+ paddingTop: spacing.xxl,
2069
+ paddingBottom: spacing.lg,
2070
+ },
2071
+ tabsRow: {
2072
+ flexDirection: 'row',
2073
+ justifyContent: 'space-around',
2074
+ alignItems: 'center',
2075
+ },
2076
+ tab: {
2077
+ flex: 1,
2078
+ paddingHorizontal: spacing.sm,
2079
+ paddingVertical: spacing.md,
2080
+ marginHorizontal: 2,
2081
+ borderBottomWidth: 3,
2082
+ borderBottomColor: colors.surface,
2083
+ borderBottomLeftRadius: 8,
2084
+ borderBottomRightRadius: 8,
2085
+ backgroundColor: 'transparent',
2086
+ },
2087
+ activeTab: {
2088
+ borderBottomColor: colors.tabSelected,
2089
+ backgroundColor: colors.background,
2090
+ borderBottomLeftRadius: 12,
2091
+ borderBottomRightRadius: 12,
2092
+ },
2093
+ tabText: {
2094
+ ...typography.styles.text12Medium,
2095
+ color: colors.textSecondary,
2096
+ textAlign: 'center',
2097
+ },
2098
+ activeTabText: {
2099
+ color: colors.tabSelected,
2100
+ fontWeight: '600',
2101
+ textAlign: 'center',
2102
+ },
2103
+ filterStatusContainer: {
2104
+ paddingHorizontal: spacing.lg,
2105
+ paddingVertical: spacing.sm,
2106
+ backgroundColor: colors.background + '80', // 50% opacity
2107
+ borderBottomWidth: 1,
2108
+ borderBottomColor: colors.border + '20', // 20% opacity
2109
+ },
2110
+ filterStatusText: {
2111
+ ...typography.styles.text12Regular,
2112
+ color: colors.textSecondary,
2113
+ textAlign: 'center',
2114
+ fontStyle: 'italic',
2115
+ },
2116
+ fdOptionsContainer: {
2117
+ paddingHorizontal: spacing.lg,
2118
+ paddingBottom: spacing.xl,
2119
+ },
2120
+ // API Status Styles
2121
+ apiStatusContainer: {
2122
+ flexDirection: 'row',
2123
+ justifyContent: 'space-between',
2124
+ alignItems: 'center',
2125
+ paddingHorizontal: spacing.lg,
2126
+ paddingVertical: spacing.sm,
2127
+ backgroundColor: colors.background,
2128
+ borderBottomWidth: 1,
2129
+ borderBottomColor: colors.border + '20', // 20% opacity
2130
+ },
2131
+ apiStatusIndicator: {
2132
+ flexDirection: 'row',
2133
+ alignItems: 'center',
2134
+ flex: 1,
2135
+ },
2136
+ apiStatusText: {
2137
+ ...typography.styles.text10Regular,
2138
+ color: colors.textSecondary,
2139
+ marginLeft: spacing.xs,
2140
+ },
2141
+ apiSuccessText: {
2142
+ ...typography.styles.text10Regular,
2143
+ color: colors.success,
2144
+ marginLeft: spacing.xs,
2145
+ },
2146
+ apiErrorText: {
2147
+ ...typography.styles.text10Regular,
2148
+ color: colors.error,
2149
+ marginLeft: spacing.xs,
2150
+ },
2151
+ refreshButton: {
2152
+ paddingHorizontal: spacing.sm,
2153
+ paddingVertical: spacing.xs,
2154
+ backgroundColor: colors.primary + '20', // 20% opacity
2155
+ borderRadius: spacing.xs,
2156
+ },
2157
+ refreshButtonText: {
2158
+ ...typography.styles.text10Medium,
2159
+ color: colors.primary,
2160
+ },
2161
+ noDataContainer: {
2162
+ padding: spacing.lg,
2163
+ alignItems: 'center',
2164
+ justifyContent: 'center',
2165
+ minHeight: 200,
2166
+ },
2167
+ noDataText: {
2168
+ ...typography.styles.text14Regular,
2169
+ color: colors.textSecondary,
2170
+ textAlign: 'center',
2171
+ lineHeight: 20,
2172
+ marginBottom: spacing.md,
2173
+ },
2174
+ showAllButton: {
2175
+ backgroundColor: colors.primary,
2176
+ paddingHorizontal: spacing.lg,
2177
+ paddingVertical: spacing.sm,
2178
+ borderRadius: spacing.sm,
2179
+ marginTop: spacing.md,
2180
+ },
2181
+ showAllButtonText: {
2182
+ ...typography.styles.text14Medium,
2183
+ color: colors.headerText,
2184
+ textAlign: 'center',
2185
+ },
2186
+ loadingOverlay: {
2187
+ position: 'absolute',
2188
+ top: 0,
2189
+ left: 0,
2190
+ right: 0,
2191
+ bottom: 0,
2192
+ backgroundColor: 'rgba(0, 0, 0, 0.3)',
2193
+ justifyContent: 'center',
2194
+ alignItems: 'center',
2195
+ zIndex: 1000,
2196
+ },
2197
+ });
2198
+
2199
+ export default FDList;