@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,800 @@
1
+
2
+ import React, { useState, useEffect, useRef } from 'react';
3
+ import { View, Text, StyleSheet, ScrollView, TouchableOpacity, Alert, Platform, Image, TextInput, TouchableWithoutFeedback, BackHandler, ActivityIndicator, KeyboardAvoidingView } from 'react-native';
4
+ import Icon from 'react-native-vector-icons/Ionicons';
5
+ import DateTimePicker from '@react-native-community/datetimepicker';
6
+ // import DatePicker from 'react-native-date-picker'; // Commented out old date picker
7
+ import SafeAreaWrapper from '../components/SafeAreaWrapper';
8
+ import { Header } from '../components';
9
+ import ActionButton from '../components/ActionButton';
10
+ import TextFieldWithLabel from '../components/TextFieldWithLabel';
11
+ import { useColors, useTypography, useTheme } from '../theme/ThemeContext';
12
+ import { useCustomerNomineeMutation, useGetCustomerApplicationDetailsMutation } from '../api/customerApi';
13
+ import { useMasterData } from '../providers/MasterDataProvider';
14
+ import { useAppSelector } from '../store';
15
+ import { getUserInfoForAPI } from '../config/appDataConfig';
16
+ import { usePreviousStateMutation } from '../api/workflowApi';
17
+ import { useFocusEffect } from '@react-navigation/native';
18
+ import { navigate } from '../navigation/helpers';
19
+ import { base64Images } from '../constants/strings/base64Images';
20
+ import { NOMINEE_STRINGS } from '../constants/strings/nominee';
21
+ import { COMMON_STRINGS } from '../constants/strings/common';
22
+
23
+ export interface NomineeDetailProps {
24
+ onGoBack?: () => void;
25
+ onSave?: (data: NomineeData) => void;
26
+ initialData?: Partial<NomineeData>;
27
+ }
28
+
29
+ export interface NomineeData {
30
+ makeNomination: string;
31
+ nomineeTitle: string;
32
+ nomineeName: string;
33
+ relationship: string;
34
+ customRelationship?: string;
35
+ dateOfBirth: string;
36
+ printNameOnCertificate: boolean;
37
+ }
38
+
39
+ const makeNominationOptions = ['Yes', 'No'];
40
+
41
+ const NomineeDetail: React.FC<NomineeDetailProps> = ({ onGoBack, onSave, initialData }) => {
42
+ const colors = useColors();
43
+ const typography = useTypography();
44
+ const { themeName } = useTheme();
45
+ const styles = createStyles(colors, typography, themeName);
46
+
47
+ const { masterData, setMasterData } = useMasterData();
48
+ const workflowInstanceId = useAppSelector((state: any) => state?.onboarding?.workflowInstanceId);
49
+ const applicationId = useAppSelector((state: any) => state?.onboarding?.applicationId);
50
+ const customerId = useAppSelector((state: any) => state?.onboarding?.customerId);
51
+ const defaultProviderId = useAppSelector((state: any) => state?.onboarding?.providerId);// Default Shriram provider ID
52
+
53
+ const [customerNominee, {
54
+ data: customerNomineeResponse,
55
+ error: customerNomineeError,
56
+ isLoading: isLoadingCustomerNominee,
57
+ }] = useCustomerNomineeMutation();
58
+ const [previousState] = usePreviousStateMutation();
59
+ const [getCustomerApplicationDetails] = useGetCustomerApplicationDetailsMutation();
60
+
61
+ // Master data state
62
+ // const [masterData, setMasterData] = useState<{
63
+ // nomineePrefix: string[];
64
+ // nomineeRelation: string[];
65
+ // }>({
66
+ // nomineePrefix: [],
67
+ // nomineeRelation: [],
68
+ // });
69
+
70
+ // Loading state for back navigation
71
+ const [isGoingBack, setIsGoingBack] = useState(false);
72
+
73
+ // Form state
74
+ const [form, setForm] = useState<NomineeData>({
75
+ makeNomination: initialData?.makeNomination || 'Yes',
76
+ nomineeTitle: '', // will be set after master data loads
77
+ nomineeName: initialData?.nomineeName || '',
78
+ relationship: '', // will be set after master data loads
79
+ customRelationship: (initialData as any)?.customRelationship || '',
80
+ dateOfBirth: initialData?.dateOfBirth || '',
81
+ printNameOnCertificate: initialData?.printNameOnCertificate ?? false,
82
+ });
83
+
84
+
85
+ // Dropdown menu state
86
+ const [openMenus, setOpenMenus] = useState({
87
+ makeNomination: false,
88
+ title: false,
89
+ relationship: false,
90
+ });
91
+
92
+ // Error state for custom relationship
93
+ const [fieldErrors, setFieldErrors] = useState<{ [key: string]: string }>({});
94
+
95
+ // Date picker state
96
+ const [showDatePicker, setShowDatePicker] = useState(false);
97
+ const datePickerLockRef = useRef(false);
98
+ const [selectedDate, setSelectedDate] = useState(new Date());
99
+
100
+ // ScrollView ref for auto-scrolling
101
+ const scrollViewRef = useRef<ScrollView>(null);
102
+
103
+ // Ref for nominee name text field
104
+ const nomineeNameRef = useRef<TextInput>(null);
105
+
106
+ // Derive nominee prefix and relationship options from master data
107
+ const normalizeOptions = (raw: any): string[] => {
108
+ if (Array.isArray(raw)) return raw.map((v) => String(v)).filter(Boolean);
109
+ if (typeof raw === 'string' && raw.trim().length) return raw.split(',').map((v) => v.trim()).filter(Boolean);
110
+ return [];
111
+ };
112
+
113
+ const mdRoot: any = (masterData as any)?.data || masterData || {};
114
+
115
+ const nomineePrefixOptions: string[] = React.useMemo(() => {
116
+ const raw = mdRoot?.nomineePrefix || mdRoot?.titleOptions || mdRoot?.prefixOptions;
117
+ const opts = normalizeOptions(raw);
118
+ return opts.length ? opts : ['Mr', 'Ms', 'Mrsww'];
119
+ }, [mdRoot]);
120
+
121
+ const nomineeRelationOptions: string[] = React.useMemo(() => {
122
+ const raw = mdRoot?.nomineeRelation || mdRoot?.relationshipOptions || mdRoot?.relations || mdRoot?.relationOptions;
123
+ const opts = normalizeOptions(raw);
124
+ return opts.length ? opts : ['Father', 'Mother', 'Spouse', 'Son', 'Daughter', 'Others'];
125
+ }, [mdRoot]);
126
+
127
+ // Do not auto-select defaults; placeholders should remain if API does not provide data
128
+ // Intentionally no-op here to avoid preselecting values
129
+ useEffect(() => {
130
+ // no-op to preserve empty state when no API data
131
+ }, [nomineePrefixOptions, nomineeRelationOptions]);
132
+
133
+ // Sync selectedDate with form.dateOfBirth when form data changes
134
+ useEffect(() => {
135
+ if (form.dateOfBirth && form.dateOfBirth.trim() !== '') {
136
+ const dateParts = form.dateOfBirth.split('/');
137
+ if (dateParts.length === 3) {
138
+ const day = parseInt(dateParts[0], 10);
139
+ const month = parseInt(dateParts[1], 10);
140
+ const year = parseInt(dateParts[2], 10);
141
+
142
+ if (day >= 1 && day <= 31 && month >= 1 && month <= 12 && year >= 1900 && year <= new Date().getFullYear()) {
143
+ const parsedDate = new Date(year, month - 1, day);
144
+ if (!isNaN(parsedDate.getTime()) && parsedDate.getDate() === day && parsedDate.getMonth() === month - 1 && parsedDate.getFullYear() === year) {
145
+ setSelectedDate(parsedDate);
146
+ }
147
+ }
148
+ }
149
+ }
150
+ }, [form.dateOfBirth]);
151
+
152
+ // Fetch customer application details on focus and set form from nominee data
153
+ useFocusEffect(
154
+ React.useCallback(() => {
155
+ let isActive = true;
156
+ (async () => {
157
+ try {
158
+ const userInfo = getUserInfoForAPI();
159
+ const req = {
160
+ providerId: defaultProviderId,
161
+ workflowInstanceId,
162
+ userreferenceid: userInfo.userReferenceId,
163
+ applicationid: applicationId,
164
+ entityid: '',
165
+ applicationId: applicationId,
166
+ customerId: customerId,
167
+ } as any;
168
+ const res = await getCustomerApplicationDetails(req).unwrap();
169
+ if (!isActive) return;
170
+ const nominee = res?.data?.nominee;
171
+ if (nominee && Object.keys(nominee).length > 0) {
172
+ // ✅ Prefill only if nominee has valid data
173
+ const makeNomination = nominee?.intend_to_nominate ? 'Yes' : 'No';
174
+ const convertFromISO = (iso: string): string => {
175
+ if (!iso) return '';
176
+ const [y, m, d] = iso.split('-');
177
+ if (y && m && d) return `${d.padStart(2, '0')}/${m.padStart(2, '0')}/${y}`;
178
+ return '';
179
+ };
180
+
181
+ setForm(prev => ({
182
+ ...prev,
183
+ makeNomination,
184
+ nomineeTitle: nominee?.prefix || '',
185
+ nomineeName: nominee?.full_name || '',
186
+ relationship: nominee?.relation || '',
187
+ dateOfBirth: convertFromISO((nominee as any)?.dob || ''),
188
+ printNameOnCertificate: !!nominee?.print_name_on_certificate,
189
+ }));
190
+ } else {
191
+ // 🧹 No nominee data — leave dropdowns empty
192
+ setForm({
193
+ makeNomination: '',
194
+ nomineeTitle: '',
195
+ nomineeName: '',
196
+ relationship: '',
197
+ dateOfBirth: '',
198
+ printNameOnCertificate: false,
199
+ });
200
+ }
201
+ } catch (e) {
202
+ // On error, do not prefill anything
203
+ setForm({
204
+ makeNomination: '',
205
+ nomineeTitle: '', // Empty to show placeholder "No"
206
+ nomineeName: '',
207
+ relationship: '', // Empty to show placeholder "No"
208
+ dateOfBirth: '',
209
+ printNameOnCertificate: false,
210
+ });
211
+ }
212
+ })();
213
+ return () => { isActive = false; };
214
+ }, [defaultProviderId, workflowInstanceId, applicationId, customerId, getCustomerApplicationDetails])
215
+ );
216
+
217
+ const closeAllMenus = () => {
218
+ setOpenMenus({ makeNomination: false, title: false, relationship: false });
219
+ };
220
+
221
+ const updateField = (field: keyof NomineeData, value: string | boolean) => {
222
+ closeAllMenus();
223
+ setForm(prev => {
224
+ // Handle dependent clearing when user selects No for nomination
225
+ if (field === 'makeNomination') {
226
+ const isYes = String(value).toLowerCase() === 'yes';
227
+ if (!isYes) {
228
+ return {
229
+ ...prev,
230
+ makeNomination: String(value),
231
+ nomineeTitle: '',
232
+ nomineeName: '',
233
+ relationship: '',
234
+ customRelationship: '',
235
+ dateOfBirth: '',
236
+ printNameOnCertificate: false,
237
+ };
238
+ }
239
+ // If switching to Yes and fields are empty, prefill with first master data options
240
+ return {
241
+ ...prev,
242
+ makeNomination: String(value),
243
+ nomineeTitle: prev.nomineeTitle || "",
244
+ relationship: prev.relationship || "",
245
+ };
246
+ }
247
+ const next = { ...prev, [field]: value } as NomineeData;
248
+ if (field === 'relationship') {
249
+ const rel = String(value || '').trim().toLowerCase();
250
+ const isOther = rel === 'other' || rel === 'others';
251
+ if (!isOther) {
252
+ (next as any).customRelationship = '';
253
+ setFieldErrors(prevErr => ({ ...prevErr, customRelationship: '' }));
254
+ }
255
+ }
256
+ return next;
257
+ });
258
+ };
259
+
260
+ const toggleMenu = (key: keyof typeof openMenus) => {
261
+ // Remove focus from nominee name field when any dropdown is clicked
262
+ nomineeNameRef.current?.blur();
263
+
264
+ setOpenMenus(prev => ({
265
+ makeNomination: key === 'makeNomination' ? !prev.makeNomination : false,
266
+ title: key === 'title' ? !prev.title : false,
267
+ relationship: key === 'relationship' ? !prev.relationship : false,
268
+ }));
269
+ };
270
+
271
+ // Date picker functions
272
+ const openDatePicker = () => {
273
+ if (datePickerLockRef.current || showDatePicker) return;
274
+ datePickerLockRef.current = true;
275
+ closeAllMenus();
276
+ // Remove focus from nominee name field when date picker is opened
277
+ nomineeNameRef.current?.blur();
278
+
279
+ // Parse existing date from field
280
+ if (form.dateOfBirth && form.dateOfBirth.trim() !== '') {
281
+ const dateParts = form.dateOfBirth.split('/');
282
+ if (dateParts.length === 3) {
283
+ const day = parseInt(dateParts[0], 10);
284
+ const month = parseInt(dateParts[1], 10);
285
+ const year = parseInt(dateParts[2], 10);
286
+
287
+ // Validate the parsed values
288
+ if (day >= 1 && day <= 31 && month >= 1 && month <= 12 && year >= 1900 && year <= new Date().getFullYear()) {
289
+ const parsedDate = new Date(year, month - 1, day);
290
+ if (!isNaN(parsedDate.getTime()) && parsedDate.getDate() === day && parsedDate.getMonth() === month - 1 && parsedDate.getFullYear() === year) {
291
+ setSelectedDate(parsedDate);
292
+ } else {
293
+ // If parsing fails, use current date
294
+ setSelectedDate(new Date());
295
+ }
296
+ } else {
297
+ // If validation fails, use current date
298
+ setSelectedDate(new Date());
299
+ }
300
+ } else {
301
+ // If format is invalid, use current date
302
+ setSelectedDate(new Date());
303
+ }
304
+ } else {
305
+ // If no date in field, use current date
306
+ setSelectedDate(new Date());
307
+ }
308
+ setShowDatePicker(true);
309
+ };
310
+
311
+ const closeDatePicker = () => {
312
+ setShowDatePicker(false);
313
+ // Release the lock shortly after closing to avoid immediate re-open from the same tap
314
+ setTimeout(() => { datePickerLockRef.current = false; }, 500);
315
+ };
316
+
317
+ const onDateChange = (event: any, selectedDate?: Date) => {
318
+
319
+ closeDatePicker();
320
+ if (Platform.OS === 'android') {
321
+ // Android fires with types: 'set' and 'dismissed'
322
+ if (event?.type === 'set' && selectedDate) {
323
+ setSelectedDate(selectedDate);
324
+
325
+ const today = new Date();
326
+ let age = today.getFullYear() - selectedDate.getFullYear();
327
+ const m = today.getMonth() - selectedDate.getMonth();
328
+ if (m < 0 || (m === 0 && today.getDate() < selectedDate.getDate())) {
329
+ age--;
330
+ }
331
+
332
+ if (age < 18) {
333
+ updateField('dateOfBirth', '');
334
+ Alert.alert('Invalid Age', 'Nominee must be at least 18 years old.');
335
+ closeDatePicker();
336
+ return;
337
+ }
338
+
339
+ const day = selectedDate.getDate().toString().padStart(2, '0');
340
+ const month = (selectedDate.getMonth() + 1).toString().padStart(2, '0');
341
+ const year = selectedDate.getFullYear().toString();
342
+ const formattedDate = `${day}/${month}/${year}`;
343
+ updateField('dateOfBirth', formattedDate);
344
+ closeDatePicker();
345
+ return;
346
+ }
347
+ if (event?.type === 'dismissed') {
348
+ closeDatePicker();
349
+ return;
350
+ }
351
+ return;
352
+ }
353
+
354
+ // iOS inline/spinner handling
355
+ if (selectedDate) {
356
+ setSelectedDate(selectedDate);
357
+ const day = selectedDate.getDate().toString().padStart(2, '0');
358
+ const month = (selectedDate.getMonth() + 1).toString().padStart(2, '0');
359
+ const year = selectedDate.getFullYear().toString();
360
+ const formattedDate = `${day}/${month}/${year}`;
361
+ updateField('dateOfBirth', formattedDate);
362
+ }
363
+ };
364
+
365
+ const renderDropdown = (
366
+ label: string,
367
+ value: string,
368
+ field: keyof NomineeData,
369
+ options: string[],
370
+ menuKey: keyof typeof openMenus,
371
+ ) => {
372
+ // Determine placeholder based on field
373
+ let placeholderText: string = NOMINEE_STRINGS.MAKE_NOMINATION_PLACEHOLDER;
374
+ if (field === 'relationship') {
375
+ placeholderText = NOMINEE_STRINGS.RELATIONSHIP_PLACEHOLDER;
376
+ } else if (field === 'nomineeTitle') {
377
+ placeholderText = NOMINEE_STRINGS.NOMINEE_TITLE_PLACEHOLDER;
378
+ }
379
+
380
+ return (
381
+ <View>
382
+ <TextFieldWithLabel
383
+ label={label}
384
+ value={value}
385
+ onChangeText={(text) => updateField(field, text)}
386
+ variant="dropdown"
387
+ options={options}
388
+ isDropdownOpen={openMenus[menuKey]}
389
+ onDropdownToggle={() => toggleMenu(menuKey)}
390
+ onDropdownSelect={(option) => {
391
+ closeAllMenus();
392
+ updateField(field, option);
393
+ setOpenMenus(prev => ({ ...prev, [menuKey]: false }));
394
+ }}
395
+ placeholder={placeholderText}
396
+ placeholderColor={themeName === 'dark' ? colors.placeholderColor : 'rgba(0,32,34,0.2)'}
397
+ />
398
+ {openMenus[menuKey] && (
399
+ <View style={styles.inlineMenu}>
400
+ {options.map(option => (
401
+ <TouchableOpacity
402
+ key={option}
403
+ style={styles.modalOption}
404
+ onPress={() => {
405
+ closeAllMenus();
406
+ updateField(field, option);
407
+ setOpenMenus(prev => ({ ...prev, [menuKey]: false }));
408
+ }}
409
+ >
410
+ <Text style={styles.modalOptionText}>{option}</Text>
411
+ </TouchableOpacity>
412
+ ))}
413
+ </View>
414
+ )}
415
+ {/* Show custom relationship field when 'Other' selected */}
416
+ {field === 'relationship' && ((String(value || '').trim().toLowerCase() === 'other') || (String(value || '').trim().toLowerCase() === 'others')) && (
417
+ <View>
418
+ <TextFieldWithLabel
419
+ label=""
420
+ containerStyle={{ marginTop: -40 }}
421
+ value={form.customRelationship || ''}
422
+ onChangeText={(text) => {
423
+ const lettersOnly = text.replace(/[^A-Za-z ]/g, '');
424
+ // Validate on change
425
+ const rel = String(form.relationship || '').trim().toLowerCase();
426
+ const isOther = rel === 'other' || rel === 'others';
427
+ if (isOther) {
428
+ if (!lettersOnly || lettersOnly.trim().length === 0) {
429
+ setFieldErrors(prev => ({ ...prev, customRelationship: 'Relationship is required' }));
430
+ } else if (lettersOnly.trim().length < 2) {
431
+ setFieldErrors(prev => ({ ...prev, customRelationship: 'Relationship must be at least 2 characters' }));
432
+ } else {
433
+ setFieldErrors(prev => ({ ...prev, customRelationship: '' }));
434
+ }
435
+ } else {
436
+ setFieldErrors(prev => ({ ...prev, customRelationship: '' }));
437
+ }
438
+ updateField('customRelationship' as any, lettersOnly);
439
+ }}
440
+ placeholder={'Enter Custom Relationship'}
441
+ placeholderColor={themeName === 'dark' ? colors.placeholderColor : 'rgba(0,0,0,0.2)'}
442
+ variant="text"
443
+ />
444
+ {fieldErrors.customRelationship ? (
445
+ <View style={styles.errorContainer}>
446
+ {Platform.OS === 'android' && (
447
+ <Icon name="warning" size={16} color={colors.error || '#FF0000'} style={styles.errorIcon} />
448
+ )}
449
+ <Text style={styles.errorText}>{fieldErrors.customRelationship}</Text>
450
+ </View>
451
+ ) : null}
452
+ </View>
453
+ )}
454
+ </View>
455
+ );
456
+ };
457
+
458
+ const renderNomineeNameFields = () => (
459
+ <View>
460
+ <Text style={styles.fieldLabel}>{NOMINEE_STRINGS.NOMINEE_NAME_LABEL}</Text>
461
+ <View style={styles.nameFieldsContainer}>
462
+ <View style={styles.titleField}>
463
+ {renderDropdown('', form.nomineeTitle, 'nomineeTitle', nomineePrefixOptions, 'title')}
464
+ </View>
465
+ <View style={styles.nameField}>
466
+ <TextFieldWithLabel
467
+ label=""
468
+ value={form.nomineeName}
469
+ onChangeText={(text) => { closeAllMenus(); updateField('nomineeName', text); }}
470
+ onFocus={() => { closeAllMenus(); }}
471
+ textInputRef={nomineeNameRef}
472
+ placeholder={NOMINEE_STRINGS.NOMINEE_NAME_PLACEHOLDER}
473
+ placeholderColor={themeName === 'dark' ? colors.placeholderColor : 'rgba(0,0,0,0.2)'}
474
+ variant="text"
475
+ />
476
+ </View>
477
+ </View>
478
+ </View>
479
+ );
480
+
481
+ const renderDateField = (
482
+ label: string,
483
+ value: string,
484
+ field: keyof NomineeData,
485
+ ) => (
486
+ <TextFieldWithLabel
487
+ label={label}
488
+ value={value}
489
+ onChangeText={(text) => updateField(field, text)}
490
+ variant="date"
491
+ placeholder="DD/MM/YYYY"
492
+ placeholderColor={themeName === 'dark' ? colors.placeholderColor : 'rgba(0,0,0,0.2)'}
493
+ maxLength={10}
494
+ dateFormat="DD/MM/YYYY"
495
+ onDatePress={() => {
496
+ if (datePickerLockRef.current || showDatePicker) return;
497
+ closeAllMenus();
498
+ openDatePicker();
499
+ }}
500
+ editable={false}
501
+ keepWhiteBackground={true}
502
+ inputStyle={{ color: 'white' }}
503
+ />
504
+ );
505
+
506
+ // Validation function to check if all required fields are filled (pure function - no setState)
507
+ const validateForm = (): boolean => {
508
+ // If makeNomination is "No", no other fields are required
509
+ if (form.makeNomination.toLowerCase() === 'no') {
510
+ return true;
511
+ }
512
+
513
+ // If makeNomination is "Yes", all nominee fields are required
514
+ const requiredFields = [
515
+ form.nomineeTitle,
516
+ form.nomineeName,
517
+ form.relationship,
518
+ form.dateOfBirth,
519
+ ];
520
+
521
+ const baseValid = requiredFields.every(field => field && field.trim().length > 0);
522
+ const rel = String(form.relationship || '').trim().toLowerCase();
523
+ const needsCustom = rel === 'other' || rel === 'others';
524
+ if (needsCustom) {
525
+ const customRel = String(form.customRelationship || '').trim();
526
+ if (!customRel || customRel.length < 2) {
527
+ return false;
528
+ }
529
+ }
530
+ return baseValid;
531
+ };
532
+
533
+ const handleSave = async () => {
534
+ try {
535
+ // Clear previous errors
536
+ setFieldErrors({});
537
+
538
+ // Validate all required fields
539
+ const rel = String(form.relationship || '').trim().toLowerCase();
540
+ const needsCustom = rel === 'other' || rel === 'others';
541
+ if (needsCustom) {
542
+ const customRel = String(form.customRelationship || '').trim();
543
+ if (!customRel) {
544
+ setFieldErrors(prev => ({ ...prev, customRelationship: 'Relationship is required' }));
545
+ Alert.alert(COMMON_STRINGS.REQUIRED_FIELD, COMMON_STRINGS.FILL_REQUIRED_FIELDS);
546
+ return;
547
+ }
548
+ if (customRel.length < 2) {
549
+ setFieldErrors(prev => ({ ...prev, customRelationship: 'Relationship must be at least 2 characters' }));
550
+ Alert.alert(COMMON_STRINGS.REQUIRED_FIELD, 'Relationship must be at least 2 characters');
551
+ return;
552
+ }
553
+ }
554
+
555
+ if (!validateForm()) {
556
+ Alert.alert(COMMON_STRINGS.REQUIRED_FIELD, COMMON_STRINGS.FILL_REQUIRED_FIELDS);
557
+ return;
558
+ }
559
+
560
+ // Get user info from app data
561
+ const userInfo = getUserInfoForAPI();
562
+
563
+ // Convert date from DD/MM/YYYY to ISO 8601 format (YYYY-MM-DD)
564
+ const convertToISO8601 = (dateString: string): string => {
565
+ if (!dateString) return '';
566
+ const [day, month, year] = dateString.split('/');
567
+ if (day && month && year) {
568
+ const isoDate = `${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')}`;
569
+ return isoDate;
570
+ }
571
+ return '';
572
+ };
573
+
574
+ const customerNomineeRequest = {
575
+ providerId: defaultProviderId,
576
+ workflowInstanceId, // from store
577
+ userreferenceid: userInfo.userReferenceId,
578
+ applicationid: applicationId,
579
+ // Only the specified parameters
580
+ customerId: customerId,
581
+ intentToNominate: form.makeNomination === 'Yes',
582
+ fullName: form.nomineeName,
583
+ nomineePrefix: form.nomineeTitle,
584
+ relation: form.relationship,
585
+ customRelation: form.customRelationship || '',
586
+ dob: convertToISO8601(form.dateOfBirth),
587
+ nomineeNameOnCertificate: form.printNameOnCertificate,
588
+ };
589
+ const response = await customerNominee(customerNomineeRequest).unwrap();
590
+ onSave?.(form);
591
+ } catch (error) {
592
+
593
+ // Show error message to user
594
+ Alert.alert(
595
+ NOMINEE_STRINGS.NOMINEE_SAVED_FAILED,
596
+ NOMINEE_STRINGS.NOMINEE_SAVE_ERROR,
597
+ [{ text: COMMON_STRINGS.OK }]
598
+ );
599
+
600
+ // Do not call onSave on failure - stay on current screen
601
+ // User can retry or correct their information
602
+ }
603
+ };
604
+
605
+ // Handler for back button (used by both header and hardware back button)
606
+ const handleBackPress = async () => {
607
+ setIsGoingBack(true);
608
+ try {
609
+ const userInfo = getUserInfoForAPI();
610
+ await previousState({
611
+ providerId: defaultProviderId,
612
+ workflowInstanceId,
613
+ userreferenceid: userInfo.userReferenceId,
614
+ applicationid: applicationId,
615
+ entityid: '',
616
+ });
617
+ } catch (e) {
618
+ // Handle error silently
619
+ } finally {
620
+ setIsGoingBack(false);
621
+ navigate('Employee');
622
+ }
623
+ };
624
+
625
+ // Handle Android hardware back button
626
+ useEffect(() => {
627
+ if (Platform.OS !== 'android') return;
628
+
629
+ const onHardwareBackPress = () => {
630
+ handleBackPress();
631
+ return true; // Prevent default behavior
632
+ };
633
+
634
+ const backHandler = BackHandler.addEventListener(
635
+ 'hardwareBackPress',
636
+ onHardwareBackPress
637
+ );
638
+
639
+ return () => backHandler.remove();
640
+ }, [defaultProviderId, workflowInstanceId, applicationId]);
641
+
642
+ return (
643
+ <SafeAreaWrapper includeTop={false} bottomPadding={25} statusBarColor="#000000" statusBarStyle="light-content">
644
+ <Header title={NOMINEE_STRINGS.NOMINEE_DETAILS_TITLE} onBackPress={handleBackPress} backgroundColor={colors.primary} />
645
+ <KeyboardAvoidingView
646
+ behavior={Platform.OS === 'ios' ? 'padding' : undefined}
647
+ style={styles.keyboardAvoidingView}
648
+ keyboardVerticalOffset={Platform.OS === 'ios' ? 0 : 0}
649
+ >
650
+ <ScrollView
651
+ ref={scrollViewRef}
652
+ style={styles.container}
653
+ showsVerticalScrollIndicator={false}
654
+ contentContainerStyle={styles.scrollContent}
655
+ scrollEnabled={!isLoadingCustomerNominee}
656
+ keyboardShouldPersistTaps="handled"
657
+ >
658
+ <TouchableWithoutFeedback onPress={isLoadingCustomerNominee ? undefined : closeAllMenus}>
659
+ <View>
660
+ {renderDropdown(
661
+ NOMINEE_STRINGS.MAKE_NOMINATION_LABEL,
662
+ form.makeNomination,
663
+ 'makeNomination',
664
+ makeNominationOptions,
665
+ 'makeNomination'
666
+ )}
667
+
668
+ {String(form.makeNomination).toLowerCase() === 'yes' && (
669
+ <>
670
+ {renderNomineeNameFields()}
671
+ {renderDropdown(NOMINEE_STRINGS.RELATIONSHIP_LABEL, form.relationship, 'relationship', nomineeRelationOptions, 'relationship')}
672
+ {renderDateField(NOMINEE_STRINGS.DATE_OF_BIRTH_LABEL, form.dateOfBirth, 'dateOfBirth')}
673
+
674
+ {/* Date Picker Modal */}
675
+ {showDatePicker && (
676
+ <View style={styles.datePickerContainer}>
677
+ <DateTimePicker
678
+ value={selectedDate}
679
+ mode="date"
680
+ display={Platform.OS === 'ios' ? 'spinner' : 'default'}
681
+ onChange={onDateChange}
682
+ maximumDate={new Date(new Date().getFullYear() - 18, new Date().getMonth(), new Date().getDate())}
683
+ themeVariant="light"
684
+ />
685
+ </View>
686
+ )}
687
+
688
+ <View style={styles.checkboxContainer}>
689
+ <TouchableOpacity
690
+ style={styles.checkbox}
691
+ onPress={() => updateField('printNameOnCertificate', !form.printNameOnCertificate)}
692
+ >
693
+ <View style={[styles.checkboxBox, form.printNameOnCertificate && styles.checkboxChecked]}>
694
+ {form.printNameOnCertificate ? (
695
+ <Image
696
+ source={{ uri: (useTheme().themeName === 'dark') ? base64Images.checkBoxDark : base64Images.filledCheckBox }}
697
+ style={{ width: 22, height: 22 }}
698
+ resizeMode="contain"
699
+ />
700
+ ) : (
701
+ (useTheme().themeName === 'dark') ? (
702
+ <Image
703
+ source={{ uri: base64Images.unCheckBoxDark }}
704
+ style={{ width: 22, height: 22 }}
705
+ resizeMode="contain"
706
+ />
707
+ ) : null
708
+ )}
709
+ </View>
710
+ </TouchableOpacity>
711
+ <Text style={styles.checkboxText}>
712
+ {NOMINEE_STRINGS.PRINT_NAME_INSTRUCTIONS}
713
+ </Text>
714
+ </View>
715
+ </>
716
+ )}
717
+ </View>
718
+ </TouchableWithoutFeedback>
719
+ </ScrollView>
720
+ </KeyboardAvoidingView>
721
+ <View style={styles.footer}>
722
+ <ActionButton
723
+ title={COMMON_STRINGS.SAVE}
724
+ onPress={() => { closeAllMenus(); handleSave(); }}
725
+ disabled={isLoadingCustomerNominee || !validateForm()}
726
+ loading={isLoadingCustomerNominee}
727
+ />
728
+ </View>
729
+ {/* Overlay to disable screen interactions during API call */}
730
+ {isLoadingCustomerNominee && (
731
+ <View style={styles.loadingOverlay} pointerEvents="auto">
732
+ </View>
733
+ )}
734
+ {/* Loading overlay for back navigation */}
735
+ {isGoingBack && (
736
+ <View style={styles.loadingOverlay} pointerEvents="auto">
737
+ <ActivityIndicator size="large" color={colors.primary} />
738
+ </View>
739
+ )}
740
+ </SafeAreaWrapper>
741
+ );
742
+ };
743
+
744
+ const createStyles = (colors: any, typography: any, themeName: string) => StyleSheet.create({
745
+ keyboardAvoidingView: {
746
+ flex: 1,
747
+ },
748
+ container: { flex: 1, paddingHorizontal: 16, paddingTop: 20 },
749
+ scrollContent: {
750
+ flexGrow: 1,
751
+ paddingBottom: 120,
752
+ },
753
+ footer: {
754
+ position: 'absolute',
755
+ left: 0,
756
+ right: 0,
757
+ bottom: 0,
758
+ backgroundColor: colors.background,
759
+ paddingTop: 8,
760
+ paddingBottom: 16,
761
+ },
762
+ fieldLabel: { ...typography.styles.bodySmall, color: colors.textLight, marginBottom: -16 },
763
+ nameFieldsContainer: { flexDirection: 'row', gap: 12, marginBottom: 0 },
764
+ titleField: { width: 80 },
765
+ nameField: { flex: 1 },
766
+ datePickerContainer: {
767
+ backgroundColor: '#FFFFFF',
768
+ borderRadius: 12,
769
+ padding: 16,
770
+ marginBottom: 20,
771
+ marginTop: 10,
772
+ },
773
+ checkboxContainer: { flexDirection: 'row', alignItems: 'flex-start', marginBottom: 40, paddingHorizontal: 4, marginTop: 16 },
774
+ checkbox: { marginRight: 12, marginTop: 2 },
775
+ checkboxBox: { width: 20, height: 20, borderWidth: 2, borderColor: themeName === 'dark' ? colors.tabSelected : colors.primary, borderRadius: 4, alignItems: 'center', justifyContent: 'center', backgroundColor: 'white' },
776
+ checkboxChecked: {
777
+ // backgroundColor: colors.primary
778
+ },
779
+ checkboxText: { ...typography.styles.bodySmall, color: colors.textLight, flex: 1, lineHeight: 18 },
780
+ inlineMenu: { backgroundColor: themeName === 'dark' ? colors.inputBackground : colors.background, borderWidth: themeName === 'dark' ? 1 : 0.5, borderColor: themeName === 'dark' ? '#ffffff' : 'rgba(0,0,0,0.2)', borderRadius: 8, marginTop: -20, marginBottom: 10, paddingHorizontal: 12, paddingVertical: 6 },
781
+ modalOption: { paddingVertical: 14 },
782
+ modalOptionText: { ...typography.styles.bodyLarge, color: colors.text },
783
+ // Added to fix TS error where these styles are referenced above
784
+ errorContainer: { flexDirection: 'row', alignItems: 'center', marginTop: -20, marginBottom: 20 },
785
+ errorIcon: { marginRight: 6 },
786
+ errorText: { fontSize: 12, color: colors.error || '#FF0000', fontWeight: '500', flex: 1 },
787
+ loadingOverlay: {
788
+ position: 'absolute',
789
+ top: 0,
790
+ left: 0,
791
+ right: 0,
792
+ bottom: 0,
793
+ backgroundColor: 'rgba(0, 0, 0, 0.3)',
794
+ justifyContent: 'center',
795
+ alignItems: 'center',
796
+ zIndex: 1000,
797
+ },
798
+ });
799
+
800
+ export default NomineeDetail;