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