@finspringinnovations/fdsdk 0.0.1

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 (315) hide show
  1. package/README.md +184 -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 +8 -0
  7. package/lib/api/baseApi.js +456 -0
  8. package/lib/api/customerApi.d.ts +855 -0
  9. package/lib/api/customerApi.js +213 -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 +32 -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 +4 -0
  33. package/lib/assets/images/images.js +10 -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 +16 -0
  39. package/lib/components/ActiveFDCard.js +95 -0
  40. package/lib/components/AmountInput.d.ts +20 -0
  41. package/lib/components/AmountInput.js +144 -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 +57 -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 +21 -0
  55. package/lib/components/FDCard.js +96 -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 +18 -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 +45 -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 +114 -0
  95. package/lib/config/appDataConfig.js +264 -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 +37 -0
  99. package/lib/config/workflowConstants.js +38 -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 +25 -0
  103. package/lib/constants/strings/base64Images.js +28 -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 +69 -0
  125. package/lib/index.js +182 -0
  126. package/lib/navigation/RootNavigator.d.ts +8 -0
  127. package/lib/navigation/RootNavigator.js +205 -0
  128. package/lib/navigation/SimpleNavigator.d.ts +11 -0
  129. package/lib/navigation/SimpleNavigator.js +107 -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 +15 -0
  133. package/lib/navigation/index.js +42 -0
  134. package/lib/navigation/types.d.ts +113 -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 +34 -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/BookFD.d.ts +0 -0
  149. package/lib/screens/BookFD.js +315 -0
  150. package/lib/screens/Employee.d.ts +18 -0
  151. package/lib/screens/Employee.js +594 -0
  152. package/lib/screens/FDCalculator.d.ts +18 -0
  153. package/lib/screens/FDCalculator.js +759 -0
  154. package/lib/screens/FDList.d.ts +27 -0
  155. package/lib/screens/FDList.js +1008 -0
  156. package/lib/screens/FindIFSC.d.ts +16 -0
  157. package/lib/screens/FindIFSC.js +248 -0
  158. package/lib/screens/Home.d.ts +0 -0
  159. package/lib/screens/Home.js +143 -0
  160. package/lib/screens/NomineeDetail.d.ts +17 -0
  161. package/lib/screens/NomineeDetail.js +592 -0
  162. package/lib/screens/PayNow.d.ts +14 -0
  163. package/lib/screens/PayNow.js +230 -0
  164. package/lib/screens/Payment.d.ts +11 -0
  165. package/lib/screens/Payment.js +191 -0
  166. package/lib/screens/PaymentStatus.d.ts +16 -0
  167. package/lib/screens/PaymentStatus.js +397 -0
  168. package/lib/screens/ReviewKYC.d.ts +21 -0
  169. package/lib/screens/ReviewKYC.js +660 -0
  170. package/lib/state/paymentSession.d.ts +8 -0
  171. package/lib/state/paymentSession.js +13 -0
  172. package/lib/store/fdListSelectedSlice.d.ts +21 -0
  173. package/lib/store/fdListSelectedSlice.js +26 -0
  174. package/lib/store/hooks.d.ts +8 -0
  175. package/lib/store/hooks.js +31 -0
  176. package/lib/store/index.d.ts +3 -0
  177. package/lib/store/index.js +8 -0
  178. package/lib/store/onboardingSlice.d.ts +12 -0
  179. package/lib/store/onboardingSlice.js +32 -0
  180. package/lib/store/store.d.ts +13 -0
  181. package/lib/store/store.js +33 -0
  182. package/lib/theme/ThemeContext.d.ts +210 -0
  183. package/lib/theme/ThemeContext.js +90 -0
  184. package/lib/theme/colors.d.ts +80 -0
  185. package/lib/theme/colors.js +85 -0
  186. package/lib/theme/index.d.ts +34 -0
  187. package/lib/theme/index.js +69 -0
  188. package/lib/theme/shadows.d.ts +53 -0
  189. package/lib/theme/shadows.js +58 -0
  190. package/lib/theme/typography.d.ts +134 -0
  191. package/lib/theme/typography.js +143 -0
  192. package/lib/types/dataTypes.d.ts +34 -0
  193. package/lib/types/dataTypes.js +2 -0
  194. package/lib/types/workflowTypes.d.ts +2 -0
  195. package/lib/types/workflowTypes.js +2 -0
  196. package/lib/utils/apiLogger.d.ts +48 -0
  197. package/lib/utils/apiLogger.js +105 -0
  198. package/lib/utils/encryption.d.ts +28 -0
  199. package/lib/utils/encryption.js +113 -0
  200. package/lib/utils/getFDData.d.ts +48 -0
  201. package/lib/utils/getFDData.js +154 -0
  202. package/lib/utils/globalData.d.ts +2 -0
  203. package/lib/utils/globalData.js +10 -0
  204. package/package.json +76 -0
  205. package/src/api/applicationApi.ts +12 -0
  206. package/src/api/bankApi.ts +42 -0
  207. package/src/api/baseApi.ts +513 -0
  208. package/src/api/customerApi.ts +291 -0
  209. package/src/api/fdApi.ts +150 -0
  210. package/src/api/fdCalculatorApi.ts +41 -0
  211. package/src/api/index.ts +29 -0
  212. package/src/api/interestRateApi.ts +143 -0
  213. package/src/api/kycApi.ts +63 -0
  214. package/src/api/masterDataApi.ts +34 -0
  215. package/src/api/nomineeApi.ts +34 -0
  216. package/src/api/onboardingApi.ts +64 -0
  217. package/src/api/panApi.ts +25 -0
  218. package/src/api/paymentApi.ts +34 -0
  219. package/src/api/workflowApi.ts +94 -0
  220. package/src/assets/images/arrow-filled.png +0 -0
  221. package/src/assets/images/arrow-left.png +0 -0
  222. package/src/assets/images/backicon.png +0 -0
  223. package/src/assets/images/calendar.png +0 -0
  224. package/src/assets/images/chevron-down.png +0 -0
  225. package/src/assets/images/chevron-down@2x.png +0 -0
  226. package/src/assets/images/chevron-down@3x.png +0 -0
  227. package/src/assets/images/images.js +8 -0
  228. package/src/components/AadhaarInput.tsx +91 -0
  229. package/src/components/ActionButton.tsx +129 -0
  230. package/src/components/ActiveFDCard.tsx +158 -0
  231. package/src/components/AmountInput.tsx +217 -0
  232. package/src/components/CheckboxOption.tsx +93 -0
  233. package/src/components/CompanyHeader.tsx +78 -0
  234. package/src/components/DropdownSelector.tsx +77 -0
  235. package/src/components/EmptyState.tsx +109 -0
  236. package/src/components/ErrorDisplay.tsx +135 -0
  237. package/src/components/FAQItem.tsx +90 -0
  238. package/src/components/FDCard.tsx +165 -0
  239. package/src/components/FormDropdown.tsx +214 -0
  240. package/src/components/FormSection.tsx +86 -0
  241. package/src/components/Header.tsx +110 -0
  242. package/src/components/IFSCSearchResultCard.tsx +139 -0
  243. package/src/components/InfoBox.tsx +55 -0
  244. package/src/components/InterestRateCard.tsx +77 -0
  245. package/src/components/LoadingIndicator.tsx +63 -0
  246. package/src/components/OTPInput.tsx +213 -0
  247. package/src/components/PaymentDetailsCard.tsx +120 -0
  248. package/src/components/PendingFDBottomSheet.tsx +235 -0
  249. package/src/components/README.md +210 -0
  250. package/src/components/SafeAreaWrapper.tsx +68 -0
  251. package/src/components/ScreenHeader.tsx +83 -0
  252. package/src/components/StatusDisplay.tsx +139 -0
  253. package/src/components/TextFieldWithLabel.tsx +502 -0
  254. package/src/components/TrustBox.tsx +63 -0
  255. package/src/components/ValidationErrorAlert.tsx +57 -0
  256. package/src/components/ValidationMessage.tsx +134 -0
  257. package/src/components/index.tsx +47 -0
  258. package/src/config/apiConfig.ts +217 -0
  259. package/src/config/appDataConfig.ts +279 -0
  260. package/src/config/encryptionConfig.ts +65 -0
  261. package/src/config/workflowConstants.ts +43 -0
  262. package/src/constants/strings/README.md +146 -0
  263. package/src/constants/strings/bank.ts +92 -0
  264. package/src/constants/strings/base64Images.ts +29 -0
  265. package/src/constants/strings/common.ts +63 -0
  266. package/src/constants/strings/employee.ts +85 -0
  267. package/src/constants/strings/faq.ts +23 -0
  268. package/src/constants/strings/fd.ts +172 -0
  269. package/src/constants/strings/home.ts +67 -0
  270. package/src/constants/strings/index.ts +21 -0
  271. package/src/constants/strings/kyc.ts +100 -0
  272. package/src/constants/strings/nominee.ts +90 -0
  273. package/src/hooks/useAuth.ts +42 -0
  274. package/src/hooks/useFDData.ts +48 -0
  275. package/src/index.tsx +173 -0
  276. package/src/navigation/RootNavigator.tsx +352 -0
  277. package/src/navigation/SimpleNavigator.tsx +107 -0
  278. package/src/navigation/helpers.ts +85 -0
  279. package/src/navigation/index.tsx +81 -0
  280. package/src/navigation/types.ts +124 -0
  281. package/src/navigation/workflowNavigator.ts +131 -0
  282. package/src/providers/ApiProvider.tsx +43 -0
  283. package/src/providers/MasterDataProvider.tsx +30 -0
  284. package/src/screens/AadhaarVerification.tsx +809 -0
  285. package/src/screens/AddBankAccount.tsx +541 -0
  286. package/src/screens/BankDetail.tsx +826 -0
  287. package/src/screens/BookFD.tsx +330 -0
  288. package/src/screens/Employee.tsx +822 -0
  289. package/src/screens/FDCalculator.tsx +987 -0
  290. package/src/screens/FDList.tsx +1284 -0
  291. package/src/screens/FindIFSC.tsx +332 -0
  292. package/src/screens/Home.tsx +152 -0
  293. package/src/screens/NomineeDetail.tsx +800 -0
  294. package/src/screens/PayNow.tsx +282 -0
  295. package/src/screens/Payment.tsx +224 -0
  296. package/src/screens/PaymentStatus.tsx +561 -0
  297. package/src/screens/ReviewKYC.tsx +956 -0
  298. package/src/state/paymentSession.ts +13 -0
  299. package/src/store/fdListSelectedSlice.ts +42 -0
  300. package/src/store/hooks.ts +27 -0
  301. package/src/store/index.ts +3 -0
  302. package/src/store/onboardingSlice.ts +37 -0
  303. package/src/store/store.ts +35 -0
  304. package/src/theme/ThemeContext.tsx +82 -0
  305. package/src/theme/colors.ts +90 -0
  306. package/src/theme/index.ts +64 -0
  307. package/src/theme/shadows.ts +61 -0
  308. package/src/theme/typography.ts +151 -0
  309. package/src/types/dataTypes.ts +37 -0
  310. package/src/types/env.d.ts +93 -0
  311. package/src/types/workflowTypes.ts +12 -0
  312. package/src/utils/apiLogger.ts +166 -0
  313. package/src/utils/encryption.ts +159 -0
  314. package/src/utils/getFDData.ts +175 -0
  315. package/src/utils/globalData.ts +7 -0
@@ -0,0 +1,1284 @@
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 } 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 } 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 } from '../config/workflowConstants';
33
+ import { setGlobalData } from '../utils/globalData';
34
+ import { FD_STRINGS } from '../constants/strings';
35
+ import { base64Images } from '../constants/strings/base64Images';
36
+
37
+ var completeFDData: any = null;
38
+
39
+ export interface FDItem {
40
+ id: string;
41
+ providerId: string; // Added providerId field
42
+ name: string;
43
+ accountNumber: string;
44
+ roi: string;
45
+ tenure: string;
46
+ amount: number;
47
+ maturityDate: string;
48
+ status: 'active' | 'matured' | 'pending';
49
+ creditRating: string;
50
+ interestPayout?: string; // Optional interest payout field
51
+ }
52
+
53
+ export interface FDListProps {
54
+ onGoBack?: () => void;
55
+ onSelectFD?: (fdId: string) => void;
56
+ onNavigateToFDCalculator?: (fdData?: any) => void;
57
+ customStyles?: {
58
+ container?: object;
59
+ header?: object;
60
+ title?: object;
61
+ fdCard?: object;
62
+ };
63
+ }
64
+
65
+ const FDList: React.FC<FDListProps> = ({
66
+ onGoBack,
67
+ onSelectFD,
68
+ onNavigateToFDCalculator,
69
+ customStyles = {},
70
+ }) => {
71
+ const [activeTab, setActiveTab] = useState<string>(FD_STRINGS.ALL_FDS_TAB);
72
+ const [showPendingFDBottomSheet, setShowPendingFDBottomSheet] = useState(false);
73
+ const [selectedFD, setSelectedFD] = useState<FDItem | null>(null);
74
+ const [isInitialized, setIsInitialized] = useState(false);
75
+ const colors = useColors();
76
+ const typography = useTypography();
77
+ const spacing = useSpacing();
78
+ const { themeName } = useTheme();
79
+ const dispatch = useAppDispatch();
80
+ const hasFetchedRatesRef = React.useRef(false);
81
+ const isCallingInterestRatesRef = React.useRef(false);
82
+
83
+ // API calls to fetch
84
+ const [getInterestRates, {
85
+ data: interestRates,
86
+ error: interestRatesError,
87
+ isLoading: isLoadingRates,
88
+ }] = useGetInterestRatesMutation();
89
+
90
+ const [getCustomerApplications, {
91
+ data: customerApplications,
92
+ error: customerApplicationsError,
93
+ isLoading: isLoadingApplications,
94
+ }] = useGetCustomerApplicationsMutation();
95
+
96
+ const [terminateWorkflow, {
97
+ data: terminateWorkflowData,
98
+ error: terminateWorkflowError,
99
+ isLoading: isTerminatingWorkflow,
100
+ }] = useTerminateWorkflowMutation();
101
+
102
+ // Payment Reverse Feed API
103
+ const [paymentReverseFeed, {
104
+ data: paymentReverseFeedResponse,
105
+ error: paymentReverseFeedError,
106
+ isLoading: isLoadingPaymentReverseFeed,
107
+ }] = usePaymentReverseFeedMutation();
108
+
109
+ const styles = createStyles(colors, typography, spacing, themeName);
110
+ const { setMasterData } = useMasterData();
111
+
112
+ // Redux selectors for workflow IDs
113
+ const workflowInstanceId = useAppSelector((state: any) => state?.onboarding?.workflowInstanceId);
114
+ const applicationId = useAppSelector((state: any) => state?.onboarding?.applicationId);
115
+ const entityId = useAppSelector((state: any) => state?.onboarding?.entityid);
116
+ const providerId = useAppSelector((state: any) => state?.onboarding?.providerId);
117
+
118
+ // Helper function to check if customer applications is empty
119
+ const isCustomerApplicationsEmpty = () => {
120
+ if (!customerApplications) {
121
+ return true;
122
+ }
123
+
124
+ // Handle direct array response
125
+ if (Array.isArray(customerApplications)) {
126
+ const isEmpty = customerApplications.length === 0;
127
+ return isEmpty;
128
+ }
129
+
130
+ // Handle response with data property
131
+ if (customerApplications.data && Array.isArray(customerApplications.data)) {
132
+ const isEmpty = customerApplications.data.length === 0;
133
+ return isEmpty;
134
+ }
135
+
136
+ // Handle response with applications property
137
+ if (customerApplications.applications && Array.isArray(customerApplications.applications)) {
138
+ const isEmpty = customerApplications.applications.length === 0;
139
+ return isEmpty;
140
+ }
141
+
142
+ // If customerApplications exists but doesn't match expected structure, consider it not empty
143
+ return false;
144
+ };
145
+
146
+ // Helper function to get provider ID from interest rates response (robust to varying shapes)
147
+ const getProviderIdFromInterestRates = () => {
148
+ try {
149
+ const data = interestRates?.data;
150
+ if (!data) return undefined;
151
+
152
+ const extractId = (rate: any) => {
153
+ return (
154
+ rate?.providerId ||
155
+ rate?.provider_id ||
156
+ rate?.fd_provider_id ||
157
+ rate?.fdProviderId ||
158
+ rate?.provider?.id ||
159
+ undefined
160
+ );
161
+ };
162
+
163
+ // Prefer SDR
164
+ const sdr = Array.isArray(data?.sdrScheme) ? data.sdrScheme : [];
165
+ for (const r of sdr) {
166
+ const id = extractId(r);
167
+ if (id) {
168
+ return id;
169
+ }
170
+ }
171
+
172
+ // Fallback to FDR
173
+ const fdr = Array.isArray(data?.fdrScheme) ? data.fdrScheme : [];
174
+ for (const r of fdr) {
175
+ const id = extractId(r);
176
+ if (id) {
177
+ return id;
178
+ }
179
+ }
180
+
181
+ return undefined;
182
+ } catch (e) {
183
+ return undefined;
184
+ }
185
+ };
186
+
187
+ // Helper function to filter FDs based on tenure
188
+ const filterFDsByTenure = (fdList: FDItem[], filter: string): FDItem[] => {
189
+ if (filter === FD_STRINGS.ALL_FDS_TAB) return fdList;
190
+
191
+ return fdList.filter(fd => {
192
+ // Extract tenure in months from the fd.tenure string (e.g., "12 Months" -> 12)
193
+ const tenureMatch = fd.tenure.match(/(\d+)\s*Months?/i);
194
+ if (!tenureMatch) return false;
195
+
196
+ const tenureMonths = parseInt(tenureMatch[1], 10);
197
+ const tenureYears = tenureMonths / 12;
198
+
199
+ switch (filter) {
200
+ case '<1Y':
201
+ return tenureYears <= 1;
202
+ case '<1-3Y':
203
+ return tenureYears > 1 && tenureYears <= 3;
204
+ case '<3-5Y':
205
+ return tenureYears > 3 && tenureYears <= 5;
206
+ case '>5Y':
207
+ return tenureYears > 5;
208
+ default:
209
+ return true;
210
+ }
211
+ });
212
+ };
213
+
214
+
215
+
216
+ // Master data fetch - use default providerId first, then update with rates data
217
+ const defaultProviderId = ''; // Default provider ID for initial call
218
+ const inferredProviderId = React.useMemo(() => {
219
+ if (interestRates?.data?.sdrScheme?.[0]?.providerId) return interestRates.data.sdrScheme[0].providerId;
220
+ if (interestRates?.data?.fdrScheme?.[0]?.providerId) return interestRates.data.fdrScheme[0].providerId;
221
+ return defaultProviderId; // Use default if no rates data yet
222
+ }, [interestRates]);
223
+
224
+ const { data: masterData, isLoading: isLoadingMaster } = useGetMasterDataQuery(
225
+ { providerId: inferredProviderId },
226
+ { skip: !inferredProviderId }
227
+ );
228
+
229
+ // Only render once all three API calls have completed (success or error)
230
+ // Show loading initially until all APIs have completed
231
+ const isAllDataReady = React.useMemo(() => {
232
+ // If not initialized yet, show loading
233
+ if (!isInitialized) return false;
234
+
235
+ // Step 1: Check if interest rates API has been called and completed
236
+ // If currently loading, show loading
237
+ if (isLoadingRates) return false;
238
+ // If not loading but no data and no error, API hasn't been called yet - show loading
239
+ if (!interestRates && !interestRatesError) return false;
240
+
241
+ // Step 2: Once interest rates are done, customer applications should be called
242
+ // If currently loading, show loading
243
+ if (isLoadingApplications) return false;
244
+ // If interest rates succeeded, we must wait for customer applications
245
+ // If interest rates failed, we can still proceed (but applications might not be called)
246
+ if (interestRates) {
247
+ // Interest rates succeeded, so applications should be called
248
+ // If not loading but no data and no error, applications haven't been called yet - show loading
249
+ if (!customerApplications && !customerApplicationsError) return false;
250
+ }
251
+
252
+ // Step 3: Check master data - only required if providerId is available
253
+ const masterShouldRun = !!inferredProviderId;
254
+ if (masterShouldRun) {
255
+ // Master data should be done (either loaded or error, but not loading)
256
+ if (isLoadingMaster) return false;
257
+ }
258
+
259
+ return true;
260
+ }, [isInitialized, interestRates, interestRatesError, isLoadingRates, customerApplications, customerApplicationsError, isLoadingApplications, inferredProviderId, isLoadingMaster]);
261
+
262
+ useEffect(() => {
263
+ if (masterData) {
264
+ setMasterData(masterData);
265
+ }
266
+ }, [masterData, setMasterData]);
267
+
268
+ // Function to refresh all data - Required sequence: Interest -> Applications -> Master
269
+ const refreshData = useCallback(async () => {
270
+ if (!isInitialized) {
271
+ return;
272
+ }
273
+
274
+ // Prevent multiple simultaneous calls
275
+ if (isCallingInterestRatesRef.current || isLoadingRates) {
276
+ return;
277
+ }
278
+
279
+ try {
280
+ isCallingInterestRatesRef.current = true;
281
+ // Add 500ms delay before calling interest rate API
282
+ await new Promise(resolve => setTimeout(resolve, 500));
283
+ await getInterestRates({});
284
+ } catch (error) {
285
+ // Handle error silently
286
+ } finally {
287
+ isCallingInterestRatesRef.current = false;
288
+ }
289
+ }, [getInterestRates, isInitialized, isLoadingRates]);
290
+
291
+ // Initialize component after mount
292
+ useEffect(() => {
293
+ const initializeComponent = async () => {
294
+ // Validate required user data before proceeding
295
+ const appData = getAppData();
296
+
297
+ const requiredFields = [
298
+ { field: 'id', label: 'User ID' },
299
+ { field: 'name', label: 'User Name' },
300
+ { field: 'dob', label: 'Date of Birth' },
301
+ { field: 'gender', label: 'Gender' },
302
+ { field: 'mobNo', label: 'Mobile Number' },
303
+ { field: 'email', label: 'Email' },
304
+ ];
305
+
306
+ const missingFields: string[] = [];
307
+
308
+ requiredFields.forEach(({ field, label }) => {
309
+ const value = appData?.[field as keyof typeof appData];
310
+ if (!value || (typeof value === 'string' && !value.trim())) {
311
+ missingFields.push(label);
312
+ }
313
+ });
314
+
315
+ // Show alert if required fields are missing
316
+ if (missingFields.length > 0) {
317
+ Alert.alert(
318
+ 'Cannot Proceed with FD Booking',
319
+ `The following required information is missing from the main app:\n\n${missingFields.join('\n')}\n\nPlease provide all required user information to proceed.`,
320
+ [
321
+ {
322
+ text: 'Cancel FD Booking',
323
+ onPress: () => {
324
+ // Redirect back to main app
325
+ if (onGoBack) {
326
+ onGoBack();
327
+ } else {
328
+ // No-op when onGoBack is not provided
329
+ }
330
+ },
331
+ style: 'cancel',
332
+ },
333
+ ],
334
+ { cancelable: false }
335
+ );
336
+ return; // Don't initialize if validation fails
337
+ }
338
+
339
+ // Additional value validation (case-insensitive): gender, maritalStatus, typeOfAccount
340
+ const errors: string[] = [];
341
+
342
+ // Gender must be Male, Female, or Other (case-insensitive)
343
+ const gender = String(appData?.gender || '').trim().toLowerCase();
344
+ const allowedGenders = ['male', 'female', 'other'];
345
+ if (gender && !allowedGenders.includes(gender)) {
346
+ errors.push("Gender must be one of: Male, Female, Other");
347
+ }
348
+
349
+ // Marital status must be Married, Unmarried, or Other (case-insensitive)
350
+ const maritalStatus = String(appData?.maritalStatus || '').trim().toLowerCase();
351
+ const allowedMarital = ['married', 'unmarried', 'other'];
352
+ if (maritalStatus && !allowedMarital.includes(maritalStatus)) {
353
+ errors.push("Marital Status must be one of: Married, Unmarried, Other");
354
+ }
355
+
356
+ // Account type must be 'Saving A/c' or 'Current A/c' (case-insensitive, tolerate spaces)
357
+ const typeOfAccountRaw = String(appData?.typeOfAccount || '').trim();
358
+ const typeOfAccount = typeOfAccountRaw.replace(/\s+/g, ' ').toLowerCase();
359
+ const allowedAccounts = ['saving a/c', 'current a/c'];
360
+ if (typeOfAccountRaw && !allowedAccounts.includes(typeOfAccount)) {
361
+ errors.push("Type of Account must be one of: 'Saving A/c', 'Current A/c'");
362
+ }
363
+
364
+ if (errors.length > 0) {
365
+ Alert.alert(
366
+ 'Invalid User Data',
367
+ `${errors.join('\n')}`,
368
+ [
369
+ {
370
+ text: 'OK',
371
+ onPress: () => {
372
+ if (onGoBack) {
373
+ onGoBack();
374
+ }
375
+ },
376
+ style: 'cancel',
377
+ },
378
+ ],
379
+ { cancelable: false }
380
+ );
381
+ return; // Stop initialization on invalid values
382
+ }
383
+
384
+ // Small delay to ensure component is fully mounted
385
+ await new Promise(resolve => setTimeout(resolve, 100));
386
+ setIsInitialized(true);
387
+ };
388
+
389
+ initializeComponent();
390
+ }, [onGoBack]);
391
+
392
+ // Handle Android hardware back button - use same navigation as header back button
393
+ useEffect(() => {
394
+ if (Platform.OS !== 'android') return;
395
+
396
+ const onHardwareBackPress = () => {
397
+ // Call onGoBack which triggers onExit in RootNavigator
398
+ if (onGoBack) {
399
+ onGoBack();
400
+ }
401
+ return true; // Prevent default behavior
402
+ };
403
+
404
+ const backHandler = BackHandler.addEventListener(
405
+ 'hardwareBackPress',
406
+ onHardwareBackPress
407
+ );
408
+
409
+ return () => backHandler.remove();
410
+ }, [onGoBack]);
411
+
412
+ // Removed init-time fetch to avoid duplicate calls; we'll fetch on first focus only
413
+
414
+ // Refresh data when screen comes into focus (only if initialized)
415
+ useFocusEffect(
416
+ useCallback(() => {
417
+ if (!isInitialized) return;
418
+ if (hasFetchedRatesRef.current) return; // ensure called only once when screen first shows
419
+ // Prevent multiple simultaneous calls
420
+ if (isCallingInterestRatesRef.current || isLoadingRates) return;
421
+ hasFetchedRatesRef.current = true;
422
+ refreshData();
423
+ }, [refreshData, isInitialized, isLoadingRates])
424
+ );
425
+
426
+ // After interest rates are received, call Customer Applications
427
+ useEffect(() => {
428
+ if (!interestRates) return;
429
+ try {
430
+ const userInfo = getUserInfoForAPI();
431
+ getCustomerApplications({ userReferenceId: userInfo.id });
432
+ } catch (e) {
433
+ }
434
+ // eslint-disable-next-line react-hooks/exhaustive-deps
435
+ }, [!!interestRates]);
436
+
437
+ // Handle API errors
438
+ useEffect(() => {
439
+ if (interestRatesError) {
440
+ Alert.alert(
441
+ 'API Error',
442
+ 'Failed to fetch interest rates. Please try again.',
443
+ [
444
+ {
445
+ text: 'Retry',
446
+ onPress: async () => {
447
+ // Prevent multiple simultaneous calls
448
+ if (isCallingInterestRatesRef.current || isLoadingRates) {
449
+ return;
450
+ }
451
+ try {
452
+ isCallingInterestRatesRef.current = true;
453
+ // Add 500ms delay before retrying interest rate API
454
+ await new Promise(resolve => setTimeout(resolve, 500));
455
+ await getInterestRates({});
456
+ } catch (error) {
457
+ // Handle error silently
458
+ } finally {
459
+ isCallingInterestRatesRef.current = false;
460
+ }
461
+ }
462
+ },
463
+ { text: 'Continue', style: 'cancel' }
464
+ ]
465
+ );
466
+ }
467
+ }, [interestRatesError, getInterestRates]);
468
+
469
+ // Log successful data fetch
470
+ useEffect(() => {
471
+ if (interestRates && interestRates.data?.sdrScheme?.length > 0) {
472
+ // Interest rates loaded successfully
473
+ }
474
+ }, [interestRates]);
475
+
476
+ // Persist providerId to onboarding store as soon as we have rates
477
+ useEffect(() => {
478
+ const pid = getProviderIdFromInterestRates();
479
+ if (pid) {
480
+ try {
481
+ dispatch(setOnboardingIds({ providerId: pid }));
482
+ } catch (e) {
483
+ // Handle error silently
484
+ }
485
+ }
486
+ }, [interestRates, dispatch]);
487
+
488
+ useEffect(() => {
489
+ if (customerApplications) {
490
+ // Customer applications loaded successfully
491
+
492
+ // Persist onboarding identifiers globally for subsequent API calls (like FDCalculator)
493
+ try {
494
+ // Extract workflow parameters from response
495
+ const responseData = customerApplications?.data || customerApplications;
496
+ let applicationData = Array.isArray(responseData) ? responseData[0] : responseData;
497
+
498
+ // Prefer the application with wf_status === 'Active'
499
+ if (Array.isArray(responseData)) {
500
+ const activeOnly = responseData.find((app: any) => {
501
+ const status = (app.wf_status || app.status || '').toString();
502
+ return status === 'Active';
503
+ });
504
+ if (activeOnly) applicationData = activeOnly;
505
+ }
506
+
507
+ if (Array.isArray(responseData)) {
508
+ completeFDData = responseData.find((app: any) => {
509
+ const status = (app.wf_status || app.status || '').toString();
510
+ return status === 'Completed';
511
+ });
512
+ setGlobalData({ completeFDData: !!completeFDData });
513
+ }
514
+ if (applicationData) {
515
+ const ids = {
516
+ workflowInstanceId: applicationData.workflow_instance_id,
517
+ applicationId: applicationData.application_id,
518
+ entityid: applicationData.entity_id,
519
+ customerId: applicationData.customer_id,
520
+ fdId: applicationData.fd_id,
521
+ currentState: applicationData.current_state,
522
+ currentTask: applicationData.current_task,
523
+ // Persist providerId from interest-rate API (prefers SDR, fallback FDR)
524
+ providerId: getProviderIdFromInterestRates(),
525
+ wfStatus: applicationData.wf_status,
526
+ };
527
+ if (dispatch && setOnboardingIds) {
528
+ dispatch(setOnboardingIds(ids));
529
+ } else {
530
+ // Dispatch or setOnboardingIds unavailable
531
+ }
532
+ }
533
+ } catch (error) {
534
+ // Handle error silently
535
+ }
536
+ }
537
+ }, [customerApplications, dispatch]);
538
+
539
+ // Unified handler: behave like bottom sheet Continue flow
540
+ const handlePendingFDContinue = async () => {
541
+ // Persist active FD data to store for use across screens
542
+ try {
543
+ if (activeFD) {
544
+ const fdListSelectedData = {
545
+ id: selectedFD?.id || 'active-fd',
546
+ providerId: selectedFD?.providerId || '',
547
+ name: activeFD.name,
548
+ accountNumber: selectedFD?.accountNumber || '',
549
+ roi: `${activeFD.returns}% p.a.`,
550
+ tenure: activeFD.maturityDate || '-',
551
+ amount: Number(activeFD.invested) || 0,
552
+ maturityDate: activeFD.maturityDate || '-',
553
+ status: 'active' as const,
554
+ creditRating: selectedFD?.creditRating || 'NA',
555
+ companyName: activeFD.name,
556
+ fdRate: `${activeFD.returns}% p.a.`,
557
+ interestPayout: activeFD.interestPayout || '',
558
+ };
559
+ dispatch(setFDListSelected(fdListSelectedData));
560
+ }
561
+ } catch (e) {
562
+ // Handle error silently
563
+ }
564
+
565
+ // Get current state from customer applications for workflow navigation
566
+ let currentState: any = null;
567
+ let transactionId: string | undefined = undefined;
568
+ try {
569
+ if (customerApplications && !isCustomerApplicationsEmpty()) {
570
+ // Normalize list
571
+ let applicationsData: any[] = [];
572
+ if (Array.isArray(customerApplications)) {
573
+ applicationsData = customerApplications;
574
+ } else if (customerApplications.data && Array.isArray(customerApplications.data)) {
575
+ applicationsData = customerApplications.data;
576
+ } else if (customerApplications.applications && Array.isArray(customerApplications.applications)) {
577
+ applicationsData = customerApplications.applications;
578
+ }
579
+
580
+ if (applicationsData.length > 0) {
581
+ // Prefer the active application
582
+ const appData = applicationsData.find((a: any) => {
583
+ const s = (a.wf_status || a.status || '').toString();
584
+ return s === 'Active';
585
+ }) || applicationsData[0];
586
+ currentState = appData.current_state;
587
+
588
+ // Extract transactionId from application data
589
+ transactionId = appData.transaction_id || appData.transactionId;
590
+ }
591
+ }
592
+ } catch (error) {
593
+ // Handle error silently
594
+ }
595
+
596
+ // If current state is payment and transactionId is available, call payment reverse feed API
597
+ if (currentState === WORKFLOW_STATES.PAYMENT && transactionId) {
598
+ try {
599
+ // Get user info and required IDs
600
+ const userInfo = getUserInfoForAPI();
601
+
602
+ // Prepare payment reverse feed request
603
+ const paymentReverseFeedRequest = {
604
+ // Headers
605
+ providerId: providerId,
606
+ workflowInstanceId: workflowInstanceId,
607
+ userreferenceid: userInfo.id,
608
+ applicationid: applicationId,
609
+ entityid: entityId,
610
+ // Body
611
+ transactionId: transactionId,
612
+ };
613
+
614
+ const response = await paymentReverseFeed(paymentReverseFeedRequest).unwrap();
615
+
616
+ // Handle the response based on payment status
617
+ const paymentStatus = (response?.data?.paymentStatus || '').toLowerCase();
618
+ const statusParam = paymentStatus === 'success' ? 'success' : paymentStatus === 'failed' ? 'failed' : 'pending';
619
+
620
+ // Build fdData for display
621
+ const fdDataParam = activeFD ? {
622
+ companyName: activeFD.name,
623
+ amount: Number(activeFD.invested) || 0,
624
+ fdRate: `${activeFD.returns}% p.a.`,
625
+ tenure: activeFD.maturityDate ? `${activeFD.maturityDate}` : '-',
626
+ interestPayout: activeFD.interestPayout || 'Yearly',
627
+ } : undefined;
628
+
629
+ navigate('PaymentStatus', { status: statusParam, transactionId, fdData: fdDataParam } as any);
630
+ return;
631
+
632
+ } catch (error) {
633
+ // Handle error silently
634
+
635
+ // On error, navigate with pending status as fallback
636
+ const fdDataParam = activeFD ? {
637
+ companyName: activeFD.name,
638
+ amount: Number(activeFD.invested) || 0,
639
+ fdRate: `${activeFD.returns}% p.a.`,
640
+ tenure: activeFD.maturityDate ? `${activeFD.maturityDate}` : '-',
641
+ interestPayout: activeFD.interestPayout || 'Yearly',
642
+ } : undefined;
643
+ navigate('PaymentStatus', { status: 'pending', transactionId, fdData: fdDataParam } as any);
644
+ return;
645
+ }
646
+ }
647
+
648
+ // Use workflow navigation based on current state (non-payment states or no transactionId)
649
+ if (currentState && Object.values(WORKFLOW_STATES).includes(currentState)) {
650
+ handleWorkflowNavigation({
651
+ workflowState: currentState,
652
+ investmentData: undefined,
653
+ customerData: undefined,
654
+ appData: undefined,
655
+ fdData: undefined,
656
+ completedApplications: [],
657
+ transactionId,
658
+ });
659
+ } else {
660
+ navigate('Employee');
661
+ }
662
+ };
663
+
664
+ // Derive identifiers for workflow termination from applications API
665
+ const terminateIdentifiers = React.useMemo(() => {
666
+ try {
667
+ if (!customerApplications || isCustomerApplicationsEmpty()) {
668
+ return {
669
+ applicationId: undefined as string | undefined,
670
+ workflowInstanceId: undefined as string | undefined,
671
+ entityId: undefined as string | undefined,
672
+ providerIdFromApp: undefined as string | undefined,
673
+ };
674
+ }
675
+
676
+ // Normalize list
677
+ let applicationsData: any[] = [];
678
+ if (Array.isArray(customerApplications)) {
679
+ applicationsData = customerApplications;
680
+ } else if (customerApplications.data && Array.isArray(customerApplications.data)) {
681
+ applicationsData = customerApplications.data;
682
+ } else if (customerApplications.applications && Array.isArray(customerApplications.applications)) {
683
+ applicationsData = customerApplications.applications;
684
+ }
685
+
686
+ if (applicationsData.length === 0) {
687
+ return {
688
+ applicationId: undefined,
689
+ workflowInstanceId: undefined,
690
+ entityId: undefined,
691
+ providerIdFromApp: undefined,
692
+ };
693
+ }
694
+
695
+ // Prefer a pending/in-progress one for termination, else first
696
+ const appForTerminate = applicationsData.find((a: any) => {
697
+ const s = (a.wf_status || a.status || '').toString().toLowerCase();
698
+ return s === 'pending' || s === 'in_progress' || s === 'in-progress';
699
+ }) || applicationsData[0];
700
+
701
+ const applicationId: string | undefined =
702
+ appForTerminate.application_id || appForTerminate.applicationId || appForTerminate.id;
703
+ const workflowInstanceId: string | undefined =
704
+ appForTerminate.workflow_instance_id || appForTerminate.workflowInstanceId || appForTerminate.workflow?.instanceId;
705
+ const entityId: string | undefined =
706
+ appForTerminate.entity_id || appForTerminate.entityId || appForTerminate.entity?.id;
707
+ const providerIdFromApp: string | undefined =
708
+ appForTerminate.provider_id || appForTerminate.providerId || appForTerminate.fd_provider_id || appForTerminate.fdProviderId;
709
+
710
+ return { applicationId, workflowInstanceId, entityId, providerIdFromApp };
711
+ } catch (e) {
712
+ return {
713
+ applicationId: undefined,
714
+ workflowInstanceId: undefined,
715
+ entityId: undefined,
716
+ providerIdFromApp: undefined,
717
+ };
718
+ }
719
+ }, [customerApplications]);
720
+
721
+
722
+ // Derive Active FD card data from customer applications response
723
+ const activeFD = React.useMemo(() => {
724
+ try {
725
+ if (!customerApplications || isCustomerApplicationsEmpty()) {
726
+ return {
727
+ name: 'Shriram Finance Ltd',
728
+ invested: 0,
729
+ value: 0,
730
+ returns: 0,
731
+ maturityDate: '--',
732
+ interestPayout: 'Yearly'
733
+ };
734
+ }
735
+
736
+ // Normalize list
737
+ let applicationsData: any[] = [];
738
+ if (Array.isArray(customerApplications)) {
739
+ applicationsData = customerApplications;
740
+ } else if (customerApplications.data && Array.isArray(customerApplications.data)) {
741
+ applicationsData = customerApplications.data;
742
+ } else if (customerApplications.applications && Array.isArray(customerApplications.applications)) {
743
+ applicationsData = customerApplications.applications;
744
+ }
745
+
746
+ if (applicationsData.length === 0) {
747
+ return {
748
+ name: 'Shriram Finance Ltd',
749
+ invested: 0,
750
+ value: 0,
751
+ returns: 0,
752
+ maturityDate: '--',
753
+ interestPayout: 'Yearly'
754
+ };
755
+ }
756
+
757
+ // Pick an active application if available, otherwise first
758
+ const activeApp = applicationsData.find((a: any) => {
759
+ const s = (a.wf_status || a.status || '').toString();
760
+ return s === 'Active';
761
+ }) || applicationsData[0];
762
+
763
+ const invested = Number(
764
+ activeApp.investment_amount ?? activeApp.amount ?? activeApp.investmentAmount ?? 0
765
+ );
766
+ const rate = Number(
767
+ activeApp.interest_rate ?? activeApp.interestRate ?? 0
768
+ );
769
+
770
+ // Prefer a provided current value, then maturity amount
771
+ const value = Number(
772
+ activeApp.current_value ?? activeApp.currentValue ?? activeApp.maturity_amount ?? activeApp.maturityAmount ?? 0
773
+ );
774
+
775
+ const returns = rate || 0; // display interest rate as returns %
776
+
777
+ // Format maturity date to 'Mon YYYY'
778
+ const maturityDateStr: string = activeApp.maturity_date || activeApp.maturityDate || '';
779
+ let maturityDate = '--';
780
+ if (maturityDateStr) {
781
+ const d = new Date(maturityDateStr);
782
+ if (!isNaN(d.getTime())) {
783
+ const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
784
+ maturityDate = `${months[d.getMonth()]} ${d.getFullYear()}`;
785
+ }
786
+ }
787
+
788
+ // Get interest payout term from activeApp
789
+ const interestPayout = activeApp.interest_payout_term || activeApp.interestPayoutTerm || activeApp.payout_term || 'Yearly';
790
+
791
+ return {
792
+ name: activeApp.fd_provider_name || activeApp.providerName || 'Shriram Finance Ltd',
793
+ invested,
794
+ value,
795
+ returns,
796
+ maturityDate,
797
+ interestPayout,
798
+ };
799
+ } catch (e) {
800
+ return {
801
+ name: 'Shriram Finance Ltd',
802
+ invested: 0,
803
+ value: 0,
804
+ returns: 0,
805
+ maturityDate: '--',
806
+ interestPayout: 'Yearly'
807
+ };
808
+ }
809
+ }, [customerApplications]);
810
+
811
+ // Helper function to check if there's an active FD
812
+ const hasActiveFD = React.useMemo(() => {
813
+ try {
814
+ if (!customerApplications || isCustomerApplicationsEmpty()) {
815
+ return false;
816
+ }
817
+
818
+ // Normalize list
819
+ let applicationsData: any[] = [];
820
+ if (Array.isArray(customerApplications)) {
821
+ applicationsData = customerApplications;
822
+ } else if (customerApplications.data && Array.isArray(customerApplications.data)) {
823
+ applicationsData = customerApplications.data;
824
+ } else if (customerApplications.applications && Array.isArray(customerApplications.applications)) {
825
+ applicationsData = customerApplications.applications;
826
+ }
827
+
828
+ if (applicationsData.length === 0) {
829
+ return false;
830
+ }
831
+
832
+ // Check if any application has wf_status as "Active"
833
+ const hasActive = applicationsData.some((a: any) => {
834
+ const s = (a.wf_status || a.status || '').toString();
835
+ return s === 'Active';
836
+ });
837
+
838
+ return hasActive;
839
+ } catch (e) {
840
+ return false;
841
+ }
842
+ }, [customerApplications]);
843
+
844
+ // Build FD options list from interest-rate API (show all FDs)
845
+ const fdList = React.useMemo(() => {
846
+ if (interestRates && (interestRates.data?.sdrScheme?.length > 0 || interestRates.data?.fdrScheme?.length > 0)) {
847
+ const sdr = Array.isArray(interestRates.data?.sdrScheme) ? interestRates.data.sdrScheme : [];
848
+ const fdr = Array.isArray(interestRates.data?.fdrScheme) ? interestRates.data.fdrScheme : [];
849
+ const combined = [...sdr, ...fdr];
850
+
851
+ // Building FD options from rates
852
+
853
+ return combined.map((rate: any, index: number) => ({
854
+ id: `${rate.providerId || 'provider'}-${rate.perdMonth || index}`,
855
+ providerId: rate.providerId || `provider-${index}`,
856
+ name: rate.providerName || '', // Use actual provider name from API
857
+ accountNumber: `FD${String(index + 1).padStart(9, '0')}`,
858
+ roi: `${Number(rate.rate ?? rate.interestRate ?? 0).toFixed(2)}% p.a.`,
859
+ tenure: `${rate.perdMonth} Months`,
860
+ amount: 5000,
861
+ maturityDate: '2025-12-31',
862
+ status: 'active' as const,
863
+ creditRating: rate.creditRating || 'AA+',
864
+ interestPayout: rate.interestPayout || rate.interest_payout_term || rate.payout_term || 'Yearly'
865
+ }));
866
+ }
867
+
868
+ // If rates are not available yet, show empty list
869
+ return [];
870
+ }, [interestRates]);
871
+
872
+
873
+ // Get filtered FD list based on active tab
874
+ const filteredFDList = React.useMemo(() => {
875
+ if (!fdList || fdList.length === 0) return [];
876
+
877
+ // Always create a new filtered array
878
+ const filtered = filterFDsByTenure([...fdList], activeTab); // spread to ensure new array
879
+
880
+ return filtered;
881
+ }, [fdList, activeTab]);
882
+
883
+
884
+ 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];
885
+
886
+ if (!isAllDataReady) {
887
+ return (
888
+ <SafeAreaWrapper
889
+ style={customStyles.container}
890
+ includeTop={false}
891
+ statusBarColor="#000000"
892
+ statusBarStyle="light-content"
893
+ >
894
+ <View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
895
+ <ActivityIndicator size="large" color={colors.primary} />
896
+ <Text style={{ marginTop: 12, color: colors.textSecondary }}>{FD_STRINGS.LOADING_FD_DATA}</Text>
897
+ </View>
898
+ </SafeAreaWrapper>
899
+ );
900
+ }
901
+
902
+ return (
903
+ <SafeAreaWrapper
904
+ style={customStyles.container}
905
+ includeTop={false}
906
+ statusBarColor="#0A1929"
907
+ statusBarStyle="light-content"
908
+ >
909
+ {/* Custom FDList Header */}
910
+ <View style={styles.customHeader}>
911
+ <TouchableOpacity
912
+ style={styles.backButton}
913
+ onPress={onGoBack}
914
+ activeOpacity={0.7}
915
+ >
916
+ <Image
917
+ source={{ uri: base64Images.backArrow }}
918
+ style={styles.backIcon}
919
+ resizeMode="contain"
920
+ />
921
+ </TouchableOpacity>
922
+
923
+ <View style={styles.headerContent}>
924
+ <Text style={styles.headerTitle}>{FD_STRINGS.CORPORATE_FDS_TITLE}</Text>
925
+ <View style={styles.poweredByContainer}>
926
+ <Text style={styles.poweredByText}>Powered by</Text>
927
+ <Image
928
+ source={{ uri: base64Images.simplfylogo }}
929
+ style={styles.simplifyLogo}
930
+ resizeMode="contain"
931
+ />
932
+ </View>
933
+ </View>
934
+ </View>
935
+
936
+ <ScrollView style={styles.content}>
937
+
938
+ {/* Active FDs Section - Only show if there's an active FD */}
939
+ {hasActiveFD && (
940
+ <View style={styles.section}>
941
+ <Text style={styles.sectionTitle}>{FD_STRINGS.ACTIVE_FDS_SECTION}</Text>
942
+
943
+ <TouchableOpacity activeOpacity={0.8} onPress={handlePendingFDContinue}>
944
+ <ActiveFDCard
945
+ name={activeFD.name}
946
+ invested={activeFD.invested}
947
+ value={activeFD.value}
948
+ returns={activeFD.returns}
949
+ maturityDate={activeFD.maturityDate}
950
+ />
951
+ </TouchableOpacity>
952
+ </View>
953
+ )}
954
+
955
+ {/* Tabs */}
956
+ <View style={styles.tabsContainer}>
957
+ <View style={styles.tabsRow}>
958
+ {tabs.map((tab) => (
959
+ <TouchableOpacity
960
+ key={tab}
961
+ style={[
962
+ styles.tab,
963
+ activeTab === tab && styles.activeTab
964
+ ]}
965
+ onPress={() => {
966
+ // Other tabs filter the data
967
+ setActiveTab(tab);
968
+ }}
969
+ >
970
+ <Text style={[
971
+ styles.tabText,
972
+ activeTab === tab && styles.activeTabText
973
+ ]}>
974
+ {tab}
975
+ </Text>
976
+ </TouchableOpacity>
977
+ ))}
978
+ </View>
979
+ </View>
980
+
981
+ {/* Filter Status removed as requested */}
982
+
983
+ {/* FD List */}
984
+ <View style={styles.fdOptionsContainer}>
985
+ {filteredFDList.length > 0 ? (
986
+ filteredFDList.map((fd: FDItem) => (
987
+ <FDCard
988
+ key={fd.id}
989
+ id={fd.id}
990
+ name={fd.name}
991
+ accountNumber={fd.accountNumber}
992
+ roi={fd.roi}
993
+ tenure={fd.tenure}
994
+ amount={fd.amount}
995
+ maturityDate={fd.maturityDate}
996
+ status={fd.status as 'active' | 'matured' | 'pending'}
997
+ creditRating={fd.creditRating}
998
+ onPress={(fdId) => {
999
+ // Log the selection
1000
+ onSelectFD?.(fdId);
1001
+
1002
+ // Store the selected FD data locally
1003
+ setSelectedFD(fd);
1004
+
1005
+ // Store the selected FD data in Redux for PayNow screen
1006
+ const fdListSelectedData = {
1007
+ id: fd.id,
1008
+ providerId: fd.providerId,
1009
+ name: fd.name,
1010
+ accountNumber: fd.accountNumber,
1011
+ roi: fd.roi,
1012
+ tenure: fd.tenure,
1013
+ amount: fd.amount,
1014
+ maturityDate: fd.maturityDate,
1015
+ status: fd.status as 'active' | 'matured' | 'pending',
1016
+ creditRating: fd.creditRating,
1017
+ // Additional fields for PayNow display
1018
+ companyName: fd.name,
1019
+ fdRate: fd.roi,
1020
+ interestPayout: fd.interestPayout || 'Yearly', // Use FD's interest payout or default
1021
+ };
1022
+
1023
+ dispatch(setFDListSelected(fdListSelectedData));
1024
+
1025
+ // Check if there's an active FD
1026
+ if (hasActiveFD) {
1027
+ // If there's an active FD, show bottom sheet
1028
+ setShowPendingFDBottomSheet(true);
1029
+ } else {
1030
+ // If no active FD, redirect to FD calculator
1031
+ onNavigateToFDCalculator?.(fd);
1032
+ }
1033
+ }}
1034
+ customStyles={customStyles}
1035
+ />
1036
+ ))
1037
+ ) : (
1038
+ <View style={styles.noDataContainer}>
1039
+ <Text style={styles.noDataText}>
1040
+ {isLoadingRates || isLoadingApplications ? FD_STRINGS.LOADING_FD_DATA :
1041
+ interestRatesError || customerApplicationsError ? FD_STRINGS.FAILED_TO_LOAD_FD_DATA :
1042
+ fdList.length === 0 ? (!isCustomerApplicationsEmpty() ? FD_STRINGS.NO_CUSTOMER_FDS_FOUND : FD_STRINGS.NO_FD_OPTIONS_AVAILABLE) :
1043
+ `${FD_STRINGS.NO_FDS_FOUND_FOR_TENURE} "${activeTab}" tenure range.`}
1044
+ </Text>
1045
+
1046
+ </View>
1047
+ )}
1048
+ </View>
1049
+ </ScrollView>
1050
+
1051
+ {/* Pending FD Bottom Sheet */}
1052
+ <PendingFDBottomSheet
1053
+ visible={showPendingFDBottomSheet}
1054
+ onClose={() => setShowPendingFDBottomSheet(false)}
1055
+ pendingFDData={activeFD}
1056
+ isBookingNewLoading={isTerminatingWorkflow}
1057
+ onContinue={async () => {
1058
+ setShowPendingFDBottomSheet(false);
1059
+ await handlePendingFDContinue();
1060
+ }}
1061
+ onBookNew={async () => {
1062
+ try {
1063
+
1064
+ // Pull identifiers from application API response, with last-resort placeholders
1065
+ const requestParams = {
1066
+ providerId: inferredProviderId,
1067
+ workflowInstanceId: terminateIdentifiers.workflowInstanceId || 'wf-instance-unknown',
1068
+ entityId: terminateIdentifiers.entityId || 'entity-unknown',
1069
+ applicationId: terminateIdentifiers.applicationId || 'application-unknown',
1070
+ } as const;
1071
+
1072
+
1073
+ // Call workflow terminate API
1074
+ await terminateWorkflow(requestParams);
1075
+
1076
+ // Dismiss bottom sheet
1077
+ setShowPendingFDBottomSheet(false);
1078
+
1079
+ // Navigate to FD calculator
1080
+ onNavigateToFDCalculator?.();
1081
+
1082
+ } catch (error) {
1083
+ // Handle error silently
1084
+
1085
+ // Still dismiss bottom sheet and navigate even if API fails
1086
+ // setShowPendingFDBottomSheet(false);
1087
+ // onNavigateToFDCalculator?.();
1088
+
1089
+ // Show error alert
1090
+ Alert.alert(
1091
+ 'Warning',
1092
+ 'Failed to terminate existing workflow, but continuing with new FD booking.',
1093
+ [{ text: 'OK' }]
1094
+ );
1095
+ }
1096
+ }}
1097
+ />
1098
+ </SafeAreaWrapper>
1099
+ );
1100
+ };
1101
+
1102
+ const createStyles = (colors: ColorScheme, typography: any, spacing: any, themeName: ThemeName) => StyleSheet.create({
1103
+ container: {
1104
+ flex: 1,
1105
+ backgroundColor: colors.surface,
1106
+ },
1107
+ customHeader: {
1108
+ backgroundColor: '#0A1929',
1109
+ paddingTop: Platform.OS === 'ios' ? 50 : 20,
1110
+ paddingBottom: spacing.md,
1111
+ paddingHorizontal: spacing.lg,
1112
+ flexDirection: 'row',
1113
+ alignItems: 'center',
1114
+ },
1115
+ backButton: {
1116
+ width: 40,
1117
+ height: 40,
1118
+ alignItems: 'center',
1119
+ justifyContent: 'center',
1120
+ },
1121
+ backIcon: {
1122
+ width: 24,
1123
+ height: 24,
1124
+ tintColor: '#FFFFFF',
1125
+ },
1126
+ headerContent: {
1127
+ flex: 1,
1128
+ alignItems: 'flex-start',
1129
+ marginRight: 40, // Balance the back button width
1130
+ },
1131
+ headerTitle: {
1132
+ ...typography.styles.h3,
1133
+ color: '#FFFFFF',
1134
+ marginBottom: spacing.xs,
1135
+ },
1136
+ poweredByContainer: {
1137
+ flexDirection: 'row',
1138
+ alignItems: 'center',
1139
+ },
1140
+ poweredByText: {
1141
+ ...typography.styles.text10Regular,
1142
+ color: '#FFFFFF',
1143
+ opacity: 0.7,
1144
+ marginRight: 0,
1145
+ },
1146
+ simplifyLogo: {
1147
+ width: 70,
1148
+ height: 20,
1149
+ },
1150
+ content: {
1151
+ flex: 1,
1152
+ },
1153
+ section: {
1154
+ paddingHorizontal: spacing.lg,
1155
+ paddingTop: spacing.xl,
1156
+ },
1157
+ sectionTitle: {
1158
+ ...typography.styles.h3,
1159
+ color: colors.text,
1160
+ marginBottom: spacing.lg,
1161
+ },
1162
+ tabsContainer: {
1163
+ paddingHorizontal: spacing.lg,
1164
+ paddingTop: spacing.xxl,
1165
+ paddingBottom: spacing.lg,
1166
+ },
1167
+ tabsRow: {
1168
+ flexDirection: 'row',
1169
+ justifyContent: 'space-around',
1170
+ alignItems: 'center',
1171
+ },
1172
+ tab: {
1173
+ flex: 1,
1174
+ paddingHorizontal: spacing.sm,
1175
+ paddingVertical: spacing.md,
1176
+ marginHorizontal: 2,
1177
+ borderBottomWidth: 3,
1178
+ borderBottomColor: 'white',
1179
+ borderBottomLeftRadius: 8,
1180
+ borderBottomRightRadius: 8,
1181
+ backgroundColor: 'transparent',
1182
+ },
1183
+ activeTab: {
1184
+ borderBottomColor: colors.tabSelected,
1185
+ backgroundColor: themeName === 'dark' ? '#000000' : '#fff',
1186
+ borderBottomLeftRadius: 12,
1187
+ borderBottomRightRadius: 12,
1188
+ },
1189
+ tabText: {
1190
+ ...typography.styles.text12Medium,
1191
+ color: colors.textSecondary,
1192
+ textAlign: 'center',
1193
+ },
1194
+ activeTabText: {
1195
+ color: colors.tabSelected,
1196
+ fontWeight: '600',
1197
+ textAlign: 'center',
1198
+ },
1199
+ filterStatusContainer: {
1200
+ paddingHorizontal: spacing.lg,
1201
+ paddingVertical: spacing.sm,
1202
+ backgroundColor: colors.background + '80', // 50% opacity
1203
+ borderBottomWidth: 1,
1204
+ borderBottomColor: colors.border + '20', // 20% opacity
1205
+ },
1206
+ filterStatusText: {
1207
+ ...typography.styles.text12Regular,
1208
+ color: colors.textSecondary,
1209
+ textAlign: 'center',
1210
+ fontStyle: 'italic',
1211
+ },
1212
+ fdOptionsContainer: {
1213
+ paddingHorizontal: spacing.lg,
1214
+ paddingBottom: spacing.xl,
1215
+ },
1216
+ // API Status Styles
1217
+ apiStatusContainer: {
1218
+ flexDirection: 'row',
1219
+ justifyContent: 'space-between',
1220
+ alignItems: 'center',
1221
+ paddingHorizontal: spacing.lg,
1222
+ paddingVertical: spacing.sm,
1223
+ backgroundColor: colors.background,
1224
+ borderBottomWidth: 1,
1225
+ borderBottomColor: colors.border + '20', // 20% opacity
1226
+ },
1227
+ apiStatusIndicator: {
1228
+ flexDirection: 'row',
1229
+ alignItems: 'center',
1230
+ flex: 1,
1231
+ },
1232
+ apiStatusText: {
1233
+ ...typography.styles.text10Regular,
1234
+ color: colors.textSecondary,
1235
+ marginLeft: spacing.xs,
1236
+ },
1237
+ apiSuccessText: {
1238
+ ...typography.styles.text10Regular,
1239
+ color: colors.success,
1240
+ marginLeft: spacing.xs,
1241
+ },
1242
+ apiErrorText: {
1243
+ ...typography.styles.text10Regular,
1244
+ color: colors.error,
1245
+ marginLeft: spacing.xs,
1246
+ },
1247
+ refreshButton: {
1248
+ paddingHorizontal: spacing.sm,
1249
+ paddingVertical: spacing.xs,
1250
+ backgroundColor: colors.primary + '20', // 20% opacity
1251
+ borderRadius: spacing.xs,
1252
+ },
1253
+ refreshButtonText: {
1254
+ ...typography.styles.text10Medium,
1255
+ color: colors.primary,
1256
+ },
1257
+ noDataContainer: {
1258
+ padding: spacing.lg,
1259
+ alignItems: 'center',
1260
+ justifyContent: 'center',
1261
+ minHeight: 200,
1262
+ },
1263
+ noDataText: {
1264
+ ...typography.styles.text14Regular,
1265
+ color: colors.textSecondary,
1266
+ textAlign: 'center',
1267
+ lineHeight: 20,
1268
+ marginBottom: spacing.md,
1269
+ },
1270
+ showAllButton: {
1271
+ backgroundColor: colors.primary,
1272
+ paddingHorizontal: spacing.lg,
1273
+ paddingVertical: spacing.sm,
1274
+ borderRadius: spacing.sm,
1275
+ marginTop: spacing.md,
1276
+ },
1277
+ showAllButtonText: {
1278
+ ...typography.styles.text14Medium,
1279
+ color: '#ffffff', // Use white color directly
1280
+ textAlign: 'center',
1281
+ },
1282
+ });
1283
+
1284
+ export default FDList;