@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,1002 @@
1
+ // FDCalculator.tsx
2
+ import React, { useState, useEffect } from 'react';
3
+ import {
4
+ View,
5
+ Text,
6
+ StyleSheet,
7
+ TouchableOpacity,
8
+ ScrollView,
9
+ ActivityIndicator,
10
+ Image,
11
+ Platform,
12
+ BackHandler,
13
+ Keyboard,
14
+ } from 'react-native';
15
+ import Icon from 'react-native-vector-icons/Ionicons';
16
+ import SafeAreaWrapper from '../components/SafeAreaWrapper';
17
+ import {
18
+ CompanyHeader,
19
+ AmountInput,
20
+ DropdownSelector,
21
+ InterestRateCard,
22
+ CheckboxOption,
23
+ InfoBox,
24
+ TrustBox,
25
+ FAQItem,
26
+ } from '../components';
27
+ import { useTypography, useColors, useTheme } from '../theme/ThemeContext';
28
+ import { useMasterData } from '../providers/MasterDataProvider';
29
+ import { useCalculateFDMutation } from '../api/fdCalculatorApi';
30
+ import { useGetInterestRatesMutation } from '../api/interestRateApi';
31
+ import { useStartOnboardingMutation } from '../api/onboardingApi';
32
+ import { useGetMasterDataQuery } from '../api/masterDataApi';
33
+ import { getAppData } from '../config/appDataConfig';
34
+ import { useAppSelector, useAppDispatch } from '../store';
35
+ // import { setFDCalculationData } from '../store/fdCalculationSlice';
36
+ import { setFDListSelected } from '../store/fdListSelectedSlice';
37
+ import { navigate } from '../navigation/helpers';
38
+ import { COMMON_STRINGS, FAQ_STRINGS, FD_STRINGS } from '../constants/strings';
39
+ import { base64Images } from '../constants/strings/base64Images';
40
+
41
+ export interface FDCalculatorProps {
42
+ onGoBack?: () => void;
43
+ onNavigateToReviewKYC?: () => void;
44
+ fdData?: {
45
+ id: string;
46
+ name: string;
47
+ accountNumber: string;
48
+ roi: string;
49
+ tenure: string;
50
+ amount: number;
51
+ maturityDate: string;
52
+ status: 'active' | 'matured' | 'pending';
53
+ creditRating: string;
54
+ };
55
+ }
56
+
57
+ const formatTenureDisplay = (months: number): string => {
58
+ if (months <= 12) {
59
+ return `${months} Months`;
60
+ }
61
+ const years = Math.floor(months / 12);
62
+ const remainingMonths = months % 12;
63
+ if (remainingMonths === 0) {
64
+ return `${years} Y`;
65
+ }
66
+ return `${years} Y ${remainingMonths} M`;
67
+ };
68
+
69
+ const parseTenureToMonths = (displayValue: string): number => {
70
+ const monthsMatch = displayValue.match(/^(\d+)\s*Months?$/i);
71
+ if (monthsMatch) {
72
+ return parseInt(monthsMatch[1], 10);
73
+ }
74
+ const yearMonthMatch = displayValue.match(/(\d+)\s*Y(?:\s+(\d+)\s*M)?/i);
75
+ if (yearMonthMatch) {
76
+ const years = parseInt(yearMonthMatch[1], 10);
77
+ const months = yearMonthMatch[2] ? parseInt(yearMonthMatch[2], 10) : 0;
78
+ return years * 12 + months;
79
+ }
80
+ const numberMatch = displayValue.match(/\d+/);
81
+ return numberMatch ? parseInt(numberMatch[0], 10) : 0;
82
+ };
83
+
84
+ const DEFAULT_FAQ_ITEMS = [
85
+ {
86
+ title: FAQ_STRINGS.FAQ_WITHDRAWAL_QUESTION,
87
+ description: FAQ_STRINGS.FAQ_WITHDRAWAL_ANSWER,
88
+ },
89
+ {
90
+ title: FAQ_STRINGS.FAQ_CREDIT_QUESTION,
91
+ description: FAQ_STRINGS.FAQ_CREDIT_ANSWER,
92
+ },
93
+ {
94
+ title: FAQ_STRINGS.FAQ_TAX_EXEMPTION_QUESTION,
95
+ description: FAQ_STRINGS.FAQ_TAX_EXEMPTION_ANSWER,
96
+ },
97
+ {
98
+ title: FAQ_STRINGS.FAQ_CUMULATIVE_QUESTION,
99
+ description: FAQ_STRINGS.FAQ_CUMULATIVE_ANSWER,
100
+ },
101
+ {
102
+ title: FAQ_STRINGS.FAQ_OPENING_TIME_QUESTION,
103
+ description: FAQ_STRINGS.FAQ_OPENING_TIME_ANSWER,
104
+ },
105
+ {
106
+ title: FAQ_STRINGS.FAQ_TDS_AVOIDANCE_QUESTION,
107
+ description: FAQ_STRINGS.FAQ_TDS_AVOIDANCE_ANSWER,
108
+ },
109
+ ] as const;
110
+
111
+ const FDCalculator: React.FC<FDCalculatorProps> = ({ onGoBack, onNavigateToReviewKYC, fdData }) => {
112
+ const typography = useTypography();
113
+ const colors = useColors();
114
+ const { themeName } = useTheme();
115
+ const styles = React.useMemo(() => createStyles(typography, colors, themeName), [typography, colors, themeName]);
116
+ const { masterData, setMasterData } = useMasterData();
117
+
118
+ // Get FD data from Redux as fallback if not passed as props
119
+ const fdListSelectedData = useAppSelector((state: any) => state?.fdListSelected);
120
+ const effectiveFdData = fdData || fdListSelectedData;
121
+
122
+
123
+ const [amount, setAmount] = useState('');
124
+ const [seniorCitizen, setSeniorCitizen] = useState(false);
125
+ const [taxResident, setTaxResident] = useState(true);
126
+ const [autoRenewal, setAutoRenewal] = useState(false);
127
+ const [expandedIndex, setExpandedIndex] = useState<number | null>(null);
128
+ const [calculationResult, setCalculationResult] = useState<any>(null);
129
+ const [debounceTimer, setDebounceTimer] = useState<NodeJS.Timeout | null>(null);
130
+ const [amountError, setAmountError] = useState<string>('');
131
+ const [taxResidentError, setTaxResidentError] = useState<string>('');
132
+ const [isAmountEditing, setIsAmountEditing] = useState<boolean>(false);
133
+
134
+ // Calculate age from DOB and set senior citizen status
135
+ const calculateAgeAndSetSeniorCitizen = React.useCallback((dob: string) => {
136
+ if (!dob) return;
137
+
138
+ const today = new Date();
139
+ const birthDate = new Date(dob);
140
+ let age = today.getFullYear() - birthDate.getFullYear();
141
+ const monthDiff = today.getMonth() - birthDate.getMonth();
142
+
143
+ if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) {
144
+ age--;
145
+ }
146
+
147
+ setSeniorCitizen(age >= 60);
148
+ }, []);
149
+
150
+ // Interest payout dropdown state - will be updated after master data is loaded
151
+ const [payoutValue, setPayoutValue] = useState<string>('');
152
+ const [showPayoutModal, setShowPayoutModal] = useState<boolean>(false);
153
+
154
+ // Map dropdown values to API values
155
+ const mapPayoutToAPI = (dropdownValue: string): string => {
156
+ const mapping: { [key: string]: string } = {
157
+ 'On Maturity': 'On Maturity',
158
+ 'Monthly': 'Monthly',
159
+ 'Quarterly': 'Quarterly',
160
+ 'Yearly': 'Yearly',
161
+ 'Half-Yearly': 'Half-Yearly',
162
+ };
163
+ return mapping[dropdownValue] || '';
164
+ };
165
+
166
+ // Check senior citizen status on component mount
167
+ React.useEffect(() => {
168
+ try {
169
+ const appData = getAppData();
170
+ if (appData?.dob) {
171
+ calculateAgeAndSetSeniorCitizen(appData.dob);
172
+ }
173
+ } catch (error) {
174
+ // Handle error silently
175
+ }
176
+ }, [calculateAgeAndSetSeniorCitizen]);
177
+
178
+ // Interest rates for Tenure options (real API)
179
+ const [getInterestRates, { data: interestRates }] = useGetInterestRatesMutation();
180
+ React.useEffect(() => {
181
+ try {
182
+ getInterestRates({});
183
+ } catch (e) {
184
+ // Handle error silently
185
+ }
186
+ }, [getInterestRates]);
187
+
188
+ // Fallback: Load master data if not available from global state
189
+ const defaultProviderId = useAppSelector((state: any) => state?.onboarding?.providerId);// Default FD provider ID
190
+
191
+ const { data: fallbackMasterData, isLoading: isLoadingFallback, error: fallbackError, refetch: refetchMasterData } = useGetMasterDataQuery(
192
+ { providerId: defaultProviderId }, // Use default provider ID
193
+ { skip: !!masterData } // Skip if master data is already available
194
+ );
195
+
196
+ // Use fallback master data if global master data is not available
197
+ // Store only the data parameter after stringifying instead of complete response
198
+ const effectiveMasterData = React.useMemo(() => {
199
+ const sourceData = masterData || fallbackMasterData;
200
+ if (!sourceData) return null;
201
+
202
+ // If sourceData has a data property, extract and stringify it
203
+ if (sourceData.data) {
204
+ return JSON.stringify(sourceData.data);
205
+ }
206
+
207
+ // If sourceData is already the data object, stringify it
208
+ return JSON.stringify(sourceData);
209
+ }, [masterData, fallbackMasterData]);
210
+
211
+ // Default payout options as fallback
212
+ // const defaultPayoutOptions = ['Maturity', 'Monthly eee', 'Quarterly', 'Half-Yearly', 'Yearly'];
213
+
214
+ const payoutOptions: string[] = React.useMemo(() => {
215
+ try {
216
+ const parsedData = JSON.parse(effectiveMasterData || '{}');
217
+ // If master data gives a single value instead of an array
218
+ const rawValue = parsedData?.interestPayoutTerm;
219
+
220
+ if (!rawValue) return [];
221
+
222
+ // Handle both array and string types
223
+ if (Array.isArray(rawValue)) {
224
+ return rawValue;
225
+ }
226
+
227
+ // If it's a comma-separated string or a single string
228
+ if (typeof rawValue === 'string') {
229
+ // e.g. "Maturity,Monthly,Quarterly" → ['Maturity', 'Monthly', 'Quarterly']
230
+ return rawValue.split(',').map(v => v.trim());
231
+ }
232
+
233
+ return [];
234
+ } catch (error) {
235
+ return [];
236
+ }
237
+ }, [effectiveMasterData]);
238
+
239
+ // Store fallback master data in global state if it becomes available
240
+ React.useEffect(() => {
241
+ if (fallbackMasterData && !masterData) {
242
+ setMasterData(fallbackMasterData);
243
+ }
244
+ }, [fallbackMasterData, masterData, setMasterData]);
245
+
246
+
247
+
248
+ // Update payout value when master data becomes available
249
+ React.useEffect(() => {
250
+ if (payoutOptions.length > 0 && !payoutValue) {
251
+ // Set "On Maturity" as default if available, otherwise use first option
252
+ const defaultOption = payoutOptions.find(option => option === 'On Maturity') || payoutOptions[0];
253
+ setPayoutValue(defaultOption);
254
+ }
255
+ }, [payoutOptions]);
256
+
257
+ React.useEffect(() => {
258
+ if (amount && payoutValue) {
259
+ // Only call if amount is valid
260
+ const numericValue = parseInt(amount.replace(/,/g, ''), 10);
261
+ if (numericValue >= 5000 && numericValue <= 50000000 && numericValue % 1000 === 0) {
262
+ handleCalculateFD(amount, payoutValue);
263
+ }
264
+ }
265
+ }, [payoutValue]);
266
+ // Master data processing complete
267
+
268
+ // FD Calculator API
269
+ const [calculateFD, { isLoading: isCalculating }] = useCalculateFDMutation();
270
+
271
+ // Onboarding API
272
+ const [startOnboarding, { isLoading: isOnboarding }] = useStartOnboardingMutation();
273
+ const [isBooking, setIsBooking] = React.useState(false);
274
+ const dispatch = useAppDispatch();
275
+ const { setOnboardingIds } = require('../store/onboardingSlice');
276
+
277
+ const tenureOptions: string[] = React.useMemo(() => {
278
+ const months: number[] = [];
279
+ const sdr = interestRates?.data?.sdrScheme || [];
280
+ const fdr = interestRates?.data?.fdrScheme || [];
281
+ sdr.forEach((r: any) => { if (typeof r?.perdMonth === 'number') months.push(r.perdMonth); });
282
+ fdr.forEach((r: any) => { if (typeof r?.perdMonth === 'number') months.push(r.perdMonth); });
283
+ const unique = Array.from(new Set(months)).sort((a, b) => a - b);
284
+ return unique.map(m => formatTenureDisplay(m));
285
+ }, [interestRates]);
286
+
287
+ useEffect(() => {
288
+ console.log("following are the tenure options at the fd",tenureOptions)
289
+ })
290
+
291
+ const [tenureValue, setTenureValue] = useState<string>('');
292
+ React.useEffect(() => {
293
+ if (tenureOptions.length > 0) {
294
+ // If effectiveFdData has tenure, use it; otherwise use first option
295
+ if (effectiveFdData?.tenure) {
296
+ const tenureMonths = parseTenureToMonths(effectiveFdData.tenure);
297
+ const formattedTenure = formatTenureDisplay(tenureMonths);
298
+ const matchingTenure = tenureOptions.find(option => option === formattedTenure);
299
+ setTenureValue(matchingTenure || formattedTenure);
300
+ } else {
301
+ setTenureValue(tenureOptions[0]);
302
+ }
303
+ }
304
+ }, [tenureOptions, effectiveFdData?.tenure]);
305
+ const [showTenureMenu, setShowTenureMenu] = useState<boolean>(false);
306
+
307
+ // Function to close all dropdown menus
308
+ const closeAllMenus = () => {
309
+ setShowTenureMenu(false);
310
+ setShowPayoutModal(false);
311
+ };
312
+
313
+ // Function to call FD calculator API
314
+ const handleCalculateFD = React.useCallback(async (currentAmount?: string, selectedPayout?: string) => {
315
+ const payoutToUse = selectedPayout || payoutValue;
316
+ const amountToUse = currentAmount || amount; // ✅ use latest if provided
317
+
318
+ if (!amountToUse || !payoutToUse) return;
319
+
320
+ try {
321
+ const amountValue = parseFloat(amountToUse.replace(/,/g, ''));
322
+ const tenureMonths = parseTenureToMonths(tenureValue);
323
+
324
+ if (isNaN(amountValue) || isNaN(tenureMonths) || amountValue <= 0 || tenureMonths <= 0) return;
325
+
326
+ const sdr = interestRates?.data?.sdrScheme || [];
327
+ const fdr = interestRates?.data?.fdrScheme || [];
328
+ const firstRate = sdr[0] || fdr[0];
329
+ const providerId = firstRate?.providerId;
330
+
331
+ const appData = getAppData();
332
+ const userDob = appData?.dob || '1990-01-01';
333
+ const isWomenDepositor = appData?.gender === 'F';
334
+
335
+ const requestData = {
336
+ principalAmount: amountValue,
337
+ tenureInMonths: tenureMonths,
338
+ dob: userDob,
339
+ isWomenDepositor,
340
+ investmentType: payoutToUse === 'On Maturity' ? 'SDR' : 'FDR',
341
+ providerId,
342
+ interestPayout: payoutToUse,
343
+ };
344
+
345
+ const result = await calculateFD(requestData).unwrap();
346
+ setCalculationResult(result);
347
+ } catch (error) {
348
+ // FD calculation failed
349
+ } finally {
350
+ setIsAmountEditing(false);
351
+ }
352
+ }, [amount, tenureValue, payoutValue, calculateFD, interestRates]);
353
+
354
+ // Debounced amount change handler
355
+ const handleAmountChange = React.useCallback((text: string) => {
356
+ setIsAmountEditing(true);
357
+ // Step 1: Always remove commas first
358
+ const unformatted = text.replace(/,/g, '');
359
+
360
+ // Step 2: Allow only numbers
361
+ let cleanedText = unformatted.replace(/[^0-9]/g, '');
362
+ if (cleanedText.length > 1 && cleanedText.startsWith('0')) {
363
+ cleanedText = cleanedText.replace(/^0+/, '');
364
+ }
365
+
366
+ // Step 3: Stop if greater than 5 crores
367
+ const numericValue = parseInt(cleanedText || '0', 10);
368
+ if (numericValue > 50000000) return;
369
+
370
+ // Step 4: Update state with raw digits (not formatted)
371
+ setAmount(cleanedText);
372
+
373
+ // Step 5: Validate
374
+ if (!cleanedText) {
375
+ setAmountError('');
376
+ setIsAmountEditing(false);
377
+ }
378
+ else if (numericValue < 5000) setAmountError(FD_STRINGS.MINIMUM_AMOUNT_ERROR);
379
+ else if (numericValue % 1000 !== 0) setAmountError(FD_STRINGS.AMOUNT_MULTIPLES_ERROR);
380
+ else setAmountError('');
381
+
382
+ // Step 6: Clear previous debounce timer
383
+ if (debounceTimer) clearTimeout(debounceTimer);
384
+
385
+ // Step 7: Debounced API call
386
+ const isValidLocal = numericValue >= 5000 && numericValue <= 50000000 && numericValue % 1000 === 0;
387
+ if (cleanedText && isValidLocal && payoutValue) {
388
+ const newTimer = setTimeout(() => {
389
+ // Pass latest cleanedText directly to avoid stale state
390
+ handleCalculateFD(cleanedText);
391
+ }, 1000);
392
+ setDebounceTimer(newTimer);
393
+ }
394
+ }, [debounceTimer, handleCalculateFD, payoutValue]);
395
+
396
+ // Function to call onboarding API
397
+ const handleStartOnboarding = React.useCallback(async (selectedPayout?: string) => {
398
+ if (isBooking) return;
399
+ setIsBooking(true);
400
+ const payoutToUse = selectedPayout || payoutValue;
401
+ try {
402
+ // Check if required fields are filled
403
+ if (!amount || !tenureValue) {
404
+ return;
405
+ }
406
+
407
+ const amountValue = parseFloat(amount.replace(/,/g, ''));
408
+ const tenureMonths = parseTenureToMonths(tenureValue);
409
+
410
+ if (isNaN(amountValue) || isNaN(tenureMonths) || amountValue <= 0 || tenureMonths <= 0) {
411
+ return;
412
+ }
413
+
414
+ // Check minimum amount requirement
415
+ if (amountValue < 5000) {
416
+ return;
417
+ }
418
+
419
+ // Get providerId from interest rates data
420
+ const sdr = interestRates?.data?.sdrScheme || [];
421
+ const fdr = interestRates?.data?.fdrScheme || [];
422
+ const firstRate = sdr[0] || fdr[0];
423
+ const providerId = firstRate?.providerId;
424
+
425
+ // Get user data for all required fields
426
+ const appData = getAppData();
427
+ const userDob = appData?.dob || '1990-01-01';
428
+ const isWomenDepositor = appData?.gender === 'F';
429
+
430
+ // Get calculated values from API response based on investment type
431
+ const investmentType = payoutValue === 'On Maturity' ? 'SDR' : 'FDR';
432
+ const calcData = investmentType === 'SDR'
433
+ ? calculationResult?.data?.sdrCalc?.[0]
434
+ : calculationResult?.data?.fdrCalc?.[0];
435
+ const interestRate = calcData?.wRoi;
436
+ const maturityAmount = calcData?.maturityAmount;
437
+
438
+ // Calculate maturity date
439
+ const maturityDate = new Date();
440
+ maturityDate.setMonth(maturityDate.getMonth() + tenureMonths);
441
+ const formattedMaturityDate = maturityDate.toISOString().split('T')[0];
442
+
443
+ const onboardingData = {
444
+ // User personal details
445
+ mobile: appData?.mobNo || '',
446
+ prefix: appData?.gender === 'M' ? 'Mr.' : (appData?.gender === 'F' ? 'Ms.' : 'Mr.'),
447
+ fullName: appData?.name || '',
448
+ firstName: appData?.name?.split(' ')[0] || '',
449
+ lastName: appData?.name?.split(' ').slice(1).join(' ') || '',
450
+ userReferenceId: appData?.id || '',
451
+ dob: userDob,
452
+ email: appData?.email || '',
453
+ gender: appData?.gender === 'M' ? 'Male' : (appData?.gender === 'F' ? 'Female' : 'Male'),
454
+
455
+ // FD details
456
+ autoRenewal: autoRenewal,
457
+ investmentAmount: amountValue,
458
+ tenureInMonths: tenureMonths,
459
+ interestPayoutTerm: payoutToUse,
460
+ interestRate: interestRate,
461
+ maturityDate: formattedMaturityDate,
462
+ maturityAmount: Math.round(maturityAmount),
463
+
464
+ // Additional fields
465
+ principalAmount: amountValue,
466
+ isWomenDepositor: isWomenDepositor,
467
+ investmentType: payoutToUse === 'On Maturity' ? 'SDR' : 'FDR',
468
+ providerId: providerId,
469
+ eventNotifyUrl: appData?.eventNotifyUrl,
470
+ };
471
+
472
+ const result = await startOnboarding(onboardingData).unwrap();
473
+
474
+ // Store FD calculation data directly into FDListSelected for PayNow screen
475
+ try {
476
+ const fdListSelectedData = {
477
+ id: effectiveFdData?.id || '',
478
+ providerId: providerId || '',
479
+ name: effectiveFdData?.name || '',
480
+ accountNumber: '',
481
+ roi: effectiveFdData?.roi || '',
482
+ tenure: tenureValue,
483
+ amount: parseInt(amount) || 0,
484
+ maturityDate: '',
485
+ status: 'pending' as const,
486
+ creditRating: effectiveFdData?.creditRating || 'NA',
487
+ companyName: effectiveFdData?.name || '',
488
+ fdRate: effectiveFdData?.roi || '',
489
+ interestPayout: payoutToUse,
490
+ };
491
+ dispatch(setFDListSelected(fdListSelectedData as any));
492
+ } catch (e) {
493
+ // Handle error silently
494
+ }
495
+
496
+ // Persist onboarding identifiers globally for subsequent API calls
497
+ try {
498
+ const ids = {
499
+ workflowInstanceId: result?.data?.workflowInstanceId,
500
+ applicationId: result?.data?.applicationId,
501
+ entityid: result?.data?.entityid,
502
+ customerId: result?.data?.customerId,
503
+ fdId: result?.data?.fdId,
504
+ };
505
+ if (dispatch && setOnboardingIds) {
506
+ dispatch(setOnboardingIds(ids));
507
+ } else {
508
+ // Dispatch or setOnboardingIds unavailable
509
+ }
510
+ } catch (e) {
511
+ // Handle error silently
512
+ }
513
+
514
+ // Navigate to ReviewKYC after successful onboarding
515
+ if (result.status === 'success' && onNavigateToReviewKYC) {
516
+ onNavigateToReviewKYC();
517
+ }
518
+ } catch (error) {
519
+ // Handle error silently
520
+ } finally {
521
+ setIsBooking(false);
522
+ }
523
+ }, [amount, tenureValue, interestRates, startOnboarding, onNavigateToReviewKYC, isBooking, payoutValue, calculationResult, autoRenewal]);
524
+
525
+ // Cleanup timer on unmount
526
+ React.useEffect(() => {
527
+ return () => {
528
+ if (debounceTimer) {
529
+ clearTimeout(debounceTimer);
530
+ }
531
+ };
532
+ }, [debounceTimer]);
533
+
534
+ // Derived validation state for amount input
535
+ const isAmountValid = React.useMemo(() => {
536
+ const amountValue = parseFloat(amount.replace(/,/g, ''));
537
+ return !!amount && !isNaN(amountValue) && amountValue >= 5000 && amountValue <= 50000000 && amountValue % 1000 === 0;
538
+ }, [amount]);
539
+
540
+ // Derived validation state for payout selection
541
+ const isPayoutValid = React.useMemo(() => {
542
+ return !!payoutValue && payoutValue.trim() !== '';
543
+ }, [payoutValue]);
544
+
545
+ // Handle tax resident checkbox change
546
+ const handleTaxResidentChange = () => {
547
+ const newValue = !taxResident;
548
+ setTaxResident(newValue);
549
+
550
+ // Show error if unchecked
551
+ if (!newValue) {
552
+ setTaxResidentError(FD_STRINGS.TAX_RESIDENT_ERROR);
553
+ } else {
554
+ setTaxResidentError('');
555
+ }
556
+ };
557
+
558
+ // Extract FAQ data from master data API response
559
+ const faqData: { title: string; description: string }[] = React.useMemo(() => {
560
+ const fallback = [...DEFAULT_FAQ_ITEMS];
561
+ try {
562
+ const parsedData = JSON.parse(effectiveMasterData || '{}');
563
+ const masterDataFaq = parsedData?.Faq || parsedData?.faq || parsedData?.faqData || parsedData?.frequentlyAskedQuestions;
564
+
565
+ if (masterDataFaq && Array.isArray(masterDataFaq) && masterDataFaq.length > 0) {
566
+ const normalized = masterDataFaq
567
+ .map((faq: any) => ({
568
+ title: faq.title || faq.question || faq.q || '',
569
+ description: faq.description || faq.answer || faq.a || ''
570
+ }))
571
+ .filter((faq: any) => faq.title && faq.description);
572
+
573
+ const containsLegacyBrand = normalized.some(item =>
574
+ /shriram/i.test(`${item.title} ${item.description}`)
575
+ );
576
+
577
+ if (!containsLegacyBrand && normalized.length > 0) {
578
+ return normalized;
579
+ }
580
+ }
581
+ } catch (error) {
582
+ // Ignore and use fallback
583
+ }
584
+ return fallback;
585
+ }, [effectiveMasterData]);
586
+
587
+ // Handle Android hardware back button - use same navigation as header back button
588
+ useEffect(() => {
589
+ if (Platform.OS !== 'android') return;
590
+
591
+ const onHardwareBackPress = () => {
592
+ navigate('FDList'); // Same as header back button
593
+ return true; // Prevent default behavior
594
+ };
595
+
596
+ const backHandler = BackHandler.addEventListener(
597
+ 'hardwareBackPress',
598
+ onHardwareBackPress
599
+ );
600
+
601
+ return () => backHandler.remove();
602
+ }, []);
603
+
604
+ return (
605
+ <SafeAreaWrapper
606
+ includeTop={false}
607
+ bottomPadding={25}
608
+ statusBarColor="#000000"
609
+ statusBarStyle="light-content"
610
+ >
611
+ {/* Header */}
612
+ <View style={styles.header}>
613
+ <TouchableOpacity onPress={() => navigate('FDList')} style={styles.backButton}>
614
+ <Image
615
+ source={{ uri: base64Images.backArrow }}
616
+ style={[styles.backIcon, { tintColor: themeName === 'dark' ? colors.headerText : undefined }]}
617
+ resizeMode="contain"
618
+ width={24}
619
+ height={24}
620
+
621
+ />
622
+ </TouchableOpacity>
623
+ </View>
624
+
625
+ <View style={{ flex: 1, justifyContent: 'space-between' }}>
626
+ <ScrollView
627
+ style={styles.scrollContainer}
628
+ contentContainerStyle={styles.scrollContent}
629
+ showsVerticalScrollIndicator={false}
630
+ onScrollBeginDrag={closeAllMenus}
631
+ scrollEnabled={!(isOnboarding || isBooking)}
632
+ keyboardShouldPersistTaps="handled"
633
+ bounces={true}
634
+ alwaysBounceVertical={true}
635
+ >
636
+ {/* Company Header */}
637
+ <CompanyHeader
638
+ companyName={effectiveFdData?.name || ''}
639
+ rating={effectiveFdData?.creditRating || 'AA+'}
640
+ />
641
+
642
+ {/* Amount Input */}
643
+ <AmountInput
644
+ label={FD_STRINGS.INVESTMENT_AMOUNT_LABEL}
645
+ value={amount}
646
+ onChangeText={handleAmountChange}
647
+ onFocus={closeAllMenus}
648
+ placeholder={FD_STRINGS.AMOUNT_PLACEHOLDER}
649
+ noteText={FD_STRINGS.AMOUNT_NOTE}
650
+ minMaxText={FD_STRINGS.AMOUNT_MIN_MAX}
651
+ errorMessage={amountError}
652
+ />
653
+
654
+ {/* Tenure Selector */}
655
+ <DropdownSelector
656
+ label={FD_STRINGS.TENURE_LABEL}
657
+ value={tenureValue || COMMON_STRINGS.SELECT}
658
+ onPress={() => {
659
+ Keyboard.dismiss();
660
+ closeAllMenus();
661
+ setShowTenureMenu(prev => !prev);
662
+ }}
663
+ />
664
+ {showTenureMenu && (
665
+ <View style={styles.inlineMenu}>
666
+ {(tenureOptions.length > 0 ? tenureOptions : [FD_STRINGS.TENURE_12_MONTHS, FD_STRINGS.TENURE_24_MONTHS, FD_STRINGS.TENURE_36_MONTHS]).map((option) => (
667
+ <TouchableOpacity
668
+ key={option}
669
+ style={styles.modalOption}
670
+ onPress={() => {
671
+ setTenureValue(option);
672
+ setShowTenureMenu(false);
673
+ // Call API after dropdown value changes
674
+ setTimeout(() => handleCalculateFD(), 100);
675
+ }}
676
+ >
677
+ <Text style={styles.modalOptionText}>{option}</Text>
678
+ </TouchableOpacity>
679
+ ))}
680
+ </View>
681
+ )}
682
+
683
+ {/* Interest Payout Selector */}
684
+ <DropdownSelector
685
+ label={FD_STRINGS.INTEREST_PAYOUT_TERM_LABEL}
686
+ value={payoutValue}
687
+ onPress={() => {
688
+ Keyboard.dismiss();
689
+ closeAllMenus();
690
+ setShowPayoutModal(prev => !prev);
691
+ }}
692
+ placeholder={FD_STRINGS.PAYOUT_MONTHLY}
693
+ />
694
+ {showPayoutModal && (
695
+ <View style={styles.inlineMenu}>
696
+ {payoutOptions
697
+ .filter((v, i, a) => !!v && a.indexOf(v) === i)
698
+ .map((option) => (
699
+ <TouchableOpacity
700
+ key={option}
701
+ style={styles.modalOption}
702
+ onPress={() => {
703
+ setPayoutValue(option);
704
+ setShowPayoutModal(false);
705
+ handleCalculateFD(amount, option);
706
+ // Pass the selected option directly to avoid stale state
707
+ // setTimeout(() => handleCalculateFD(option), 100);
708
+ }}
709
+ >
710
+ <Text style={styles.modalOptionText}>{option}</Text>
711
+ </TouchableOpacity>
712
+ ))}
713
+ </View>
714
+ )}
715
+
716
+ {/* Interest Rate Card */}
717
+ {isCalculating && (
718
+ <View style={styles.loadingContainer}>
719
+ <ActivityIndicator size="small" color={colors.primary} />
720
+ <Text style={styles.loadingText}>{COMMON_STRINGS.PROCESSING}</Text>
721
+ </View>
722
+ )}
723
+ <InterestRateCard
724
+ interestRate={(() => {
725
+ const data = calculationResult?.data;
726
+ const investmentType = payoutValue === 'On Maturity' ? 'SDR' : 'FDR';
727
+ const calcData = investmentType === 'SDR'
728
+ ? data?.sdrCalc?.[0]
729
+ : data?.fdrCalc?.[0];
730
+ const wroi = calcData?.wRoi || data?.wroi || data?.interestRate || data?.rate;
731
+ return wroi ? `${Number(wroi).toFixed(2)}% p.a.` : "";
732
+ })()}
733
+ maturityAmount={(() => {
734
+ const data = calculationResult?.data;
735
+ const investmentType = payoutValue === 'On Maturity' ? 'SDR' : 'FDR';
736
+ const calcData = investmentType === 'SDR'
737
+ ? data?.sdrCalc?.[0]
738
+ : data?.fdrCalc?.[0];
739
+ const maturityAmount = calcData?.maturityAmount || data?.maturityAmount || data?.totalAmount || data?.finalAmount || data?.amount;
740
+ return maturityAmount ? `₹${maturityAmount.toLocaleString()}` : "";
741
+ })()}
742
+ >
743
+ <CheckboxOption
744
+ label={FD_STRINGS.SENIOR_CITIZEN_BENEFIT}
745
+ checked={seniorCitizen}
746
+ onPress={() => setSeniorCitizen(!seniorCitizen)}
747
+ numberOfLines={1}
748
+ containerStyle={{ marginTop: 12 }}
749
+ disabled={true}
750
+ />
751
+ </InterestRateCard>
752
+
753
+ {/* Tax Resident Checkbox */}
754
+ <View style={[styles.checkboxCard, !taxResident && styles.checkboxCardError]}>
755
+ <CheckboxOption
756
+ label={FD_STRINGS.TAX_RESIDENT_CONFIRMATION}
757
+ checked={taxResident}
758
+ onPress={handleTaxResidentChange}
759
+ />
760
+ </View>
761
+ {taxResidentError && (
762
+ <View style={styles.errorContainer}>
763
+ {Platform.OS === 'android' && (
764
+ <Icon name="warning" size={16} color={colors.error || '#FF0000'} style={styles.errorIcon} />
765
+ )}
766
+ <Text style={styles.errorText}>{taxResidentError}</Text>
767
+ </View>
768
+ )}
769
+
770
+ {/* Auto Renewal Checkbox */}
771
+ <View style={styles.checkboxCard}>
772
+ <CheckboxOption
773
+ label={FD_STRINGS.MATURITY_AUTO_RENEWAL}
774
+ checked={autoRenewal}
775
+ onPress={() => setAutoRenewal(!autoRenewal)}
776
+ />
777
+ </View>
778
+
779
+ {/* Info Box with nonStock icon before withdrawal text */}
780
+ <InfoBox message={FD_STRINGS.WITHDRAWAL_INFO} containerStyle={styles.infoBoxCard} showNonStockIcon={true} />
781
+
782
+ {/* Safety & Trust */}
783
+ <Text style={styles.sectionTitle}>{FD_STRINGS.SAFETY_TRUST_TITLE}</Text>
784
+ <View style={styles.trustRow}>
785
+ <TrustBox title1={FD_STRINGS.TRUSTED_BY} title2={FD_STRINGS.CRORES_INDIANS} />
786
+ <TrustBox title1={FD_STRINGS.AAA_RATING} title2={FD_STRINGS.RATING} style={{ marginLeft: 10 }} />
787
+ </View>
788
+
789
+ {/* FAQ Section */}
790
+ <Text style={styles.sectionTitle}>{FD_STRINGS.FAQ_TITLE}</Text>
791
+ {faqData.map((item, index) => (
792
+ <FAQItem
793
+ key={index}
794
+ question={item.title}
795
+ answer={item.description}
796
+ isExpanded={expandedIndex === index}
797
+ onToggle={() => setExpandedIndex(expandedIndex === index ? null : index)}
798
+ />
799
+ ))}
800
+
801
+
802
+ </ScrollView>
803
+ {/* Start FD Button */}
804
+ <TouchableOpacity
805
+ style={[
806
+ styles.startButton,
807
+ (!taxResident || isOnboarding || isBooking || !isAmountValid || !isPayoutValid || isCalculating || isAmountEditing) ? styles.startButtonDisabled : null
808
+ ]}
809
+ disabled={!taxResident || isOnboarding || isBooking || !isAmountValid || !isPayoutValid || isCalculating || isAmountEditing}
810
+ activeOpacity={(!taxResident || isOnboarding || isBooking || !isAmountValid || !isPayoutValid || isCalculating || isAmountEditing) ? 1 : 0.7}
811
+ onPress={() => {
812
+ if (taxResident && !isOnboarding && !isBooking && isAmountValid && isPayoutValid && !isCalculating && !isAmountEditing) {
813
+ handleStartOnboarding(payoutValue);
814
+ }
815
+ }}
816
+ >
817
+ {(isOnboarding || isBooking) ? (
818
+ <View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'center' }}>
819
+ <ActivityIndicator size="small" color={themeName === 'dark' ? colors.buttonTextColor : colors.background} style={{ marginRight: 8 }} />
820
+ <Text style={[
821
+ styles.startButtonText,
822
+ (!taxResident || isOnboarding || isBooking || !isAmountValid || !isPayoutValid || isCalculating || isAmountEditing) ? styles.startButtonTextDisabled : null
823
+ ]}>
824
+ {COMMON_STRINGS.PROCESSING}
825
+ </Text>
826
+ </View>
827
+ ) : (
828
+ <Text style={[
829
+ styles.startButtonText,
830
+ (!taxResident || isOnboarding || isBooking || !isAmountValid || !isPayoutValid || isCalculating || isAmountEditing) ? styles.startButtonTextDisabled : null
831
+ ]}>
832
+ {FD_STRINGS.BOOK_FD_BUTTON}
833
+ </Text>
834
+ )}
835
+ </TouchableOpacity>
836
+ </View>
837
+ {/* Overlay to disable screen interactions during API call */}
838
+ {
839
+ (isOnboarding || isBooking) && (
840
+ <View style={styles.loadingOverlay} pointerEvents="auto">
841
+ </View>
842
+ )
843
+ }
844
+
845
+ {/* Inline dropdown renders just under the field; closes on selection */}
846
+ </SafeAreaWrapper >
847
+ );
848
+ };
849
+
850
+ export default FDCalculator;
851
+
852
+ const isAndroid15OrHigher = Platform.OS === "android" && Platform.Version >= 15;
853
+ const createStyles = (typography: any, colors: any, themeName: string) => StyleSheet.create({
854
+ header: {
855
+ flexDirection: 'row',
856
+ justifyContent: 'flex-start',
857
+ alignItems: 'center',
858
+ paddingHorizontal: 16,
859
+ paddingTop: Platform.OS === 'ios' ? 40 : 20,
860
+ paddingBottom: 10,
861
+ backgroundColor: colors.background,
862
+ },
863
+ backIcon: {
864
+ // Icon styling handled by Ionicons component
865
+ width: 24,
866
+ height: 24,
867
+ resizeMode: 'contain',
868
+ // backgroundColor: "red",
869
+ },
870
+ backButton: {
871
+ padding: 8,
872
+ marginTop: isAndroid15OrHigher ? 10 : 0,
873
+ },
874
+ scrollContainer: {
875
+ flex: 1,
876
+ },
877
+ scrollContent: {
878
+ paddingHorizontal: 16,
879
+ paddingBottom: 40,
880
+ },
881
+ checkboxCard: {
882
+ backgroundColor: themeName === 'dark' ? colors.inputBackground : 'rgba(0,235,180,0.1)',
883
+ padding: 12,
884
+ borderRadius: 4,
885
+ marginTop: 16,
886
+ },
887
+ infoBoxCard: {
888
+ backgroundColor: themeName === 'dark' ? colors.inputBackground : 'rgba(0,235,180,0.1)',
889
+ padding: 16,
890
+ borderRadius: 4,
891
+ marginTop: 16,
892
+ },
893
+ sectionTitle: {
894
+ ...typography.styles.bodyMedium,
895
+ fontWeight: typography.fontWeight.semiBold,
896
+ marginTop: 25,
897
+ marginBottom: 8,
898
+ color: colors.text,
899
+ fontSize: 16,
900
+ },
901
+ trustRow: {
902
+ flexDirection: 'row',
903
+ justifyContent: 'space-between',
904
+ marginTop: 10,
905
+ },
906
+ startButtonWrapper: {
907
+ position: 'relative',
908
+ },
909
+ startButton: {
910
+ backgroundColor: themeName === 'dark' ? colors.buttonBackground : colors.headerBg,
911
+ paddingVertical: themeName === 'dark' ? 1 : 20,
912
+ paddingHorizontal: 20,
913
+ borderRadius: themeName === 'dark' ? 10 : 30,
914
+ marginTop: 30,
915
+ alignItems: 'center',
916
+ justifyContent: 'center',
917
+ height: themeName === 'dark' ? 50 : 56,
918
+ width: '90%',
919
+ alignSelf: 'center',
920
+ },
921
+ startButtonDisabled: {
922
+ backgroundColor: '#909090',
923
+ color: '#ffffff',
924
+ },
925
+ startButtonText: {
926
+ ...typography.styles.button,
927
+ color: themeName === 'dark' ? colors.buttonTextColor : colors.background,
928
+ lineHeight: themeName === 'dark' ? 24 : typography.styles.button.lineHeight,
929
+ fontSize: 16,
930
+ },
931
+ startButtonTextDisabled: {
932
+ color: themeName === 'dark' ? "#ffffff" : colors.tabSelected,
933
+ },
934
+ inlineMenu: {
935
+ backgroundColor: themeName === 'dark' ? colors.inputBackground : colors.background,
936
+ borderWidth: themeName === 'dark' ? 1 : 0.5,
937
+ borderColor: themeName === 'dark' ? '#ffffff' : 'rgba(0,0,0,0.2)',
938
+ borderRadius: 8,
939
+ marginTop: 6,
940
+ paddingHorizontal: 12,
941
+ paddingVertical: 6,
942
+ },
943
+ modalOption: {
944
+ paddingVertical: 14,
945
+ },
946
+ modalOptionText: {
947
+ ...typography.styles.bodyLarge,
948
+ color: colors.text,
949
+ fontSize: 16,
950
+ placeholderTextColor: '#9ca9b0',
951
+ },
952
+
953
+ loadingContainer: {
954
+ flexDirection: 'row',
955
+ alignItems: 'center',
956
+ justifyContent: 'center',
957
+ paddingVertical: 12,
958
+ fontSize: 14,
959
+ marginTop: 16,
960
+ },
961
+ loadingText: {
962
+ ...typography.styles.bodyMedium,
963
+ color: colors.textLight,
964
+ marginLeft: 8,
965
+ },
966
+ overlayAboveInterestRate: {
967
+ position: 'absolute',
968
+ top: 0,
969
+ left: 0,
970
+ right: 0,
971
+ height: 20,
972
+ backgroundColor: 'rgba(128, 128, 128, 0.3)', // Light gray translucent overlay
973
+ zIndex: 1,
974
+ },
975
+ errorContainer: {
976
+ flexDirection: 'row',
977
+ alignItems: 'center',
978
+ marginTop: 8,
979
+ },
980
+ errorIcon: {
981
+ marginRight: 6,
982
+ },
983
+ errorText: {
984
+ fontSize: 14,
985
+ color: colors.error || '#FF0000',
986
+ fontWeight: '500',
987
+ flex: 1,
988
+ },
989
+ checkboxCardError: {
990
+ backgroundColor: 'rgba(255, 0, 0, 0.08)', // Red with same opacity as the original green
991
+ },
992
+ loadingOverlay: {
993
+ position: 'absolute',
994
+ top: 0,
995
+ left: 0,
996
+ right: 0,
997
+ bottom: 0,
998
+ backgroundColor: 'rgba(0, 0, 0, 0.3)',
999
+ zIndex: 1000,
1000
+ },
1001
+
1002
+ });