@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.
- package/README.md +184 -0
- package/lib/api/applicationApi.d.ts +1 -0
- package/lib/api/applicationApi.js +11 -0
- package/lib/api/bankApi.d.ts +352 -0
- package/lib/api/bankApi.js +54 -0
- package/lib/api/baseApi.d.ts +8 -0
- package/lib/api/baseApi.js +456 -0
- package/lib/api/customerApi.d.ts +855 -0
- package/lib/api/customerApi.js +213 -0
- package/lib/api/fdApi.d.ts +979 -0
- package/lib/api/fdApi.js +112 -0
- package/lib/api/fdCalculatorApi.d.ts +179 -0
- package/lib/api/fdCalculatorApi.js +36 -0
- package/lib/api/index.d.ts +14 -0
- package/lib/api/index.js +45 -0
- package/lib/api/interestRateApi.d.ts +585 -0
- package/lib/api/interestRateApi.js +101 -0
- package/lib/api/kycApi.d.ts +486 -0
- package/lib/api/kycApi.js +71 -0
- package/lib/api/masterDataApi.d.ts +158 -0
- package/lib/api/masterDataApi.js +32 -0
- package/lib/api/nomineeApi.d.ts +325 -0
- package/lib/api/nomineeApi.js +46 -0
- package/lib/api/onboardingApi.d.ts +192 -0
- package/lib/api/onboardingApi.js +41 -0
- package/lib/api/panApi.d.ts +0 -0
- package/lib/api/panApi.js +23 -0
- package/lib/api/paymentApi.d.ts +325 -0
- package/lib/api/paymentApi.js +46 -0
- package/lib/api/workflowApi.d.ts +654 -0
- package/lib/api/workflowApi.js +90 -0
- package/lib/assets/images/images.d.ts +4 -0
- package/lib/assets/images/images.js +10 -0
- package/lib/components/AadhaarInput.d.ts +13 -0
- package/lib/components/AadhaarInput.js +47 -0
- package/lib/components/ActionButton.d.ts +12 -0
- package/lib/components/ActionButton.js +87 -0
- package/lib/components/ActiveFDCard.d.ts +16 -0
- package/lib/components/ActiveFDCard.js +95 -0
- package/lib/components/AmountInput.d.ts +20 -0
- package/lib/components/AmountInput.js +144 -0
- package/lib/components/CheckboxOption.d.ts +11 -0
- package/lib/components/CheckboxOption.js +41 -0
- package/lib/components/CompanyHeader.d.ts +7 -0
- package/lib/components/CompanyHeader.js +57 -0
- package/lib/components/DropdownSelector.d.ts +9 -0
- package/lib/components/DropdownSelector.js +49 -0
- package/lib/components/EmptyState.d.ts +17 -0
- package/lib/components/EmptyState.js +44 -0
- package/lib/components/ErrorDisplay.d.ts +17 -0
- package/lib/components/ErrorDisplay.js +69 -0
- package/lib/components/FAQItem.d.ts +9 -0
- package/lib/components/FAQItem.js +52 -0
- package/lib/components/FDCard.d.ts +21 -0
- package/lib/components/FDCard.js +96 -0
- package/lib/components/FormDropdown.d.ts +18 -0
- package/lib/components/FormDropdown.js +155 -0
- package/lib/components/FormSection.d.ts +14 -0
- package/lib/components/FormSection.js +38 -0
- package/lib/components/Header.d.ts +14 -0
- package/lib/components/Header.js +52 -0
- package/lib/components/IFSCSearchResultCard.d.ts +13 -0
- package/lib/components/IFSCSearchResultCard.js +70 -0
- package/lib/components/InfoBox.d.ts +8 -0
- package/lib/components/InfoBox.js +39 -0
- package/lib/components/InterestRateCard.d.ts +8 -0
- package/lib/components/InterestRateCard.js +46 -0
- package/lib/components/LoadingIndicator.d.ts +12 -0
- package/lib/components/LoadingIndicator.js +30 -0
- package/lib/components/OTPInput.d.ts +17 -0
- package/lib/components/OTPInput.js +144 -0
- package/lib/components/PaymentDetailsCard.d.ts +20 -0
- package/lib/components/PaymentDetailsCard.js +68 -0
- package/lib/components/PendingFDBottomSheet.d.ts +18 -0
- package/lib/components/PendingFDBottomSheet.js +122 -0
- package/lib/components/SafeAreaWrapper.d.ts +13 -0
- package/lib/components/SafeAreaWrapper.js +41 -0
- package/lib/components/ScreenHeader.d.ts +11 -0
- package/lib/components/ScreenHeader.js +46 -0
- package/lib/components/StatusDisplay.d.ts +15 -0
- package/lib/components/StatusDisplay.js +88 -0
- package/lib/components/TextFieldWithLabel.d.ts +46 -0
- package/lib/components/TextFieldWithLabel.js +326 -0
- package/lib/components/TrustBox.d.ts +8 -0
- package/lib/components/TrustBox.js +45 -0
- package/lib/components/ValidationErrorAlert.d.ts +23 -0
- package/lib/components/ValidationErrorAlert.js +39 -0
- package/lib/components/ValidationMessage.d.ts +9 -0
- package/lib/components/ValidationMessage.js +98 -0
- package/lib/components/index.d.ts +35 -0
- package/lib/components/index.js +64 -0
- package/lib/config/apiConfig.d.ts +34 -0
- package/lib/config/apiConfig.js +158 -0
- package/lib/config/appDataConfig.d.ts +114 -0
- package/lib/config/appDataConfig.js +264 -0
- package/lib/config/encryptionConfig.d.ts +21 -0
- package/lib/config/encryptionConfig.js +61 -0
- package/lib/config/workflowConstants.d.ts +37 -0
- package/lib/config/workflowConstants.js +38 -0
- package/lib/constants/strings/bank.d.ts +72 -0
- package/lib/constants/strings/bank.js +86 -0
- package/lib/constants/strings/base64Images.d.ts +25 -0
- package/lib/constants/strings/base64Images.js +28 -0
- package/lib/constants/strings/common.d.ts +53 -0
- package/lib/constants/strings/common.js +62 -0
- package/lib/constants/strings/employee.d.ts +61 -0
- package/lib/constants/strings/employee.js +77 -0
- package/lib/constants/strings/faq.d.ts +14 -0
- package/lib/constants/strings/faq.js +20 -0
- package/lib/constants/strings/fd.d.ts +122 -0
- package/lib/constants/strings/fd.js +151 -0
- package/lib/constants/strings/home.d.ts +49 -0
- package/lib/constants/strings/home.js +62 -0
- package/lib/constants/strings/index.d.ts +16 -0
- package/lib/constants/strings/index.js +44 -0
- package/lib/constants/strings/kyc.d.ts +80 -0
- package/lib/constants/strings/kyc.js +94 -0
- package/lib/constants/strings/nominee.d.ts +64 -0
- package/lib/constants/strings/nominee.js +81 -0
- package/lib/hooks/useAuth.d.ts +25 -0
- package/lib/hooks/useAuth.js +39 -0
- package/lib/hooks/useFDData.d.ts +11 -0
- package/lib/hooks/useFDData.js +40 -0
- package/lib/index.d.ts +69 -0
- package/lib/index.js +182 -0
- package/lib/navigation/RootNavigator.d.ts +8 -0
- package/lib/navigation/RootNavigator.js +205 -0
- package/lib/navigation/SimpleNavigator.d.ts +11 -0
- package/lib/navigation/SimpleNavigator.js +107 -0
- package/lib/navigation/helpers.d.ts +11 -0
- package/lib/navigation/helpers.js +83 -0
- package/lib/navigation/index.d.ts +15 -0
- package/lib/navigation/index.js +42 -0
- package/lib/navigation/types.d.ts +113 -0
- package/lib/navigation/types.js +2 -0
- package/lib/navigation/workflowNavigator.d.ts +22 -0
- package/lib/navigation/workflowNavigator.js +104 -0
- package/lib/providers/ApiProvider.d.ts +7 -0
- package/lib/providers/ApiProvider.js +34 -0
- package/lib/providers/MasterDataProvider.d.ts +10 -0
- package/lib/providers/MasterDataProvider.js +54 -0
- package/lib/screens/AadhaarVerification.d.ts +7 -0
- package/lib/screens/AadhaarVerification.js +627 -0
- package/lib/screens/AddBankAccount.d.ts +22 -0
- package/lib/screens/AddBankAccount.js +381 -0
- package/lib/screens/BankDetail.d.ts +16 -0
- package/lib/screens/BankDetail.js +596 -0
- package/lib/screens/BookFD.d.ts +0 -0
- package/lib/screens/BookFD.js +315 -0
- package/lib/screens/Employee.d.ts +18 -0
- package/lib/screens/Employee.js +594 -0
- package/lib/screens/FDCalculator.d.ts +18 -0
- package/lib/screens/FDCalculator.js +759 -0
- package/lib/screens/FDList.d.ts +27 -0
- package/lib/screens/FDList.js +1008 -0
- package/lib/screens/FindIFSC.d.ts +16 -0
- package/lib/screens/FindIFSC.js +248 -0
- package/lib/screens/Home.d.ts +0 -0
- package/lib/screens/Home.js +143 -0
- package/lib/screens/NomineeDetail.d.ts +17 -0
- package/lib/screens/NomineeDetail.js +592 -0
- package/lib/screens/PayNow.d.ts +14 -0
- package/lib/screens/PayNow.js +230 -0
- package/lib/screens/Payment.d.ts +11 -0
- package/lib/screens/Payment.js +191 -0
- package/lib/screens/PaymentStatus.d.ts +16 -0
- package/lib/screens/PaymentStatus.js +397 -0
- package/lib/screens/ReviewKYC.d.ts +21 -0
- package/lib/screens/ReviewKYC.js +660 -0
- package/lib/state/paymentSession.d.ts +8 -0
- package/lib/state/paymentSession.js +13 -0
- package/lib/store/fdListSelectedSlice.d.ts +21 -0
- package/lib/store/fdListSelectedSlice.js +26 -0
- package/lib/store/hooks.d.ts +8 -0
- package/lib/store/hooks.js +31 -0
- package/lib/store/index.d.ts +3 -0
- package/lib/store/index.js +8 -0
- package/lib/store/onboardingSlice.d.ts +12 -0
- package/lib/store/onboardingSlice.js +32 -0
- package/lib/store/store.d.ts +13 -0
- package/lib/store/store.js +33 -0
- package/lib/theme/ThemeContext.d.ts +210 -0
- package/lib/theme/ThemeContext.js +90 -0
- package/lib/theme/colors.d.ts +80 -0
- package/lib/theme/colors.js +85 -0
- package/lib/theme/index.d.ts +34 -0
- package/lib/theme/index.js +69 -0
- package/lib/theme/shadows.d.ts +53 -0
- package/lib/theme/shadows.js +58 -0
- package/lib/theme/typography.d.ts +134 -0
- package/lib/theme/typography.js +143 -0
- package/lib/types/dataTypes.d.ts +34 -0
- package/lib/types/dataTypes.js +2 -0
- package/lib/types/workflowTypes.d.ts +2 -0
- package/lib/types/workflowTypes.js +2 -0
- package/lib/utils/apiLogger.d.ts +48 -0
- package/lib/utils/apiLogger.js +105 -0
- package/lib/utils/encryption.d.ts +28 -0
- package/lib/utils/encryption.js +113 -0
- package/lib/utils/getFDData.d.ts +48 -0
- package/lib/utils/getFDData.js +154 -0
- package/lib/utils/globalData.d.ts +2 -0
- package/lib/utils/globalData.js +10 -0
- package/package.json +76 -0
- package/src/api/applicationApi.ts +12 -0
- package/src/api/bankApi.ts +42 -0
- package/src/api/baseApi.ts +513 -0
- package/src/api/customerApi.ts +291 -0
- package/src/api/fdApi.ts +150 -0
- package/src/api/fdCalculatorApi.ts +41 -0
- package/src/api/index.ts +29 -0
- package/src/api/interestRateApi.ts +143 -0
- package/src/api/kycApi.ts +63 -0
- package/src/api/masterDataApi.ts +34 -0
- package/src/api/nomineeApi.ts +34 -0
- package/src/api/onboardingApi.ts +64 -0
- package/src/api/panApi.ts +25 -0
- package/src/api/paymentApi.ts +34 -0
- package/src/api/workflowApi.ts +94 -0
- package/src/assets/images/arrow-filled.png +0 -0
- package/src/assets/images/arrow-left.png +0 -0
- package/src/assets/images/backicon.png +0 -0
- package/src/assets/images/calendar.png +0 -0
- package/src/assets/images/chevron-down.png +0 -0
- package/src/assets/images/chevron-down@2x.png +0 -0
- package/src/assets/images/chevron-down@3x.png +0 -0
- package/src/assets/images/images.js +8 -0
- package/src/components/AadhaarInput.tsx +91 -0
- package/src/components/ActionButton.tsx +129 -0
- package/src/components/ActiveFDCard.tsx +158 -0
- package/src/components/AmountInput.tsx +217 -0
- package/src/components/CheckboxOption.tsx +93 -0
- package/src/components/CompanyHeader.tsx +78 -0
- package/src/components/DropdownSelector.tsx +77 -0
- package/src/components/EmptyState.tsx +109 -0
- package/src/components/ErrorDisplay.tsx +135 -0
- package/src/components/FAQItem.tsx +90 -0
- package/src/components/FDCard.tsx +165 -0
- package/src/components/FormDropdown.tsx +214 -0
- package/src/components/FormSection.tsx +86 -0
- package/src/components/Header.tsx +110 -0
- package/src/components/IFSCSearchResultCard.tsx +139 -0
- package/src/components/InfoBox.tsx +55 -0
- package/src/components/InterestRateCard.tsx +77 -0
- package/src/components/LoadingIndicator.tsx +63 -0
- package/src/components/OTPInput.tsx +213 -0
- package/src/components/PaymentDetailsCard.tsx +120 -0
- package/src/components/PendingFDBottomSheet.tsx +235 -0
- package/src/components/README.md +210 -0
- package/src/components/SafeAreaWrapper.tsx +68 -0
- package/src/components/ScreenHeader.tsx +83 -0
- package/src/components/StatusDisplay.tsx +139 -0
- package/src/components/TextFieldWithLabel.tsx +502 -0
- package/src/components/TrustBox.tsx +63 -0
- package/src/components/ValidationErrorAlert.tsx +57 -0
- package/src/components/ValidationMessage.tsx +134 -0
- package/src/components/index.tsx +47 -0
- package/src/config/apiConfig.ts +217 -0
- package/src/config/appDataConfig.ts +279 -0
- package/src/config/encryptionConfig.ts +65 -0
- package/src/config/workflowConstants.ts +43 -0
- package/src/constants/strings/README.md +146 -0
- package/src/constants/strings/bank.ts +92 -0
- package/src/constants/strings/base64Images.ts +29 -0
- package/src/constants/strings/common.ts +63 -0
- package/src/constants/strings/employee.ts +85 -0
- package/src/constants/strings/faq.ts +23 -0
- package/src/constants/strings/fd.ts +172 -0
- package/src/constants/strings/home.ts +67 -0
- package/src/constants/strings/index.ts +21 -0
- package/src/constants/strings/kyc.ts +100 -0
- package/src/constants/strings/nominee.ts +90 -0
- package/src/hooks/useAuth.ts +42 -0
- package/src/hooks/useFDData.ts +48 -0
- package/src/index.tsx +173 -0
- package/src/navigation/RootNavigator.tsx +352 -0
- package/src/navigation/SimpleNavigator.tsx +107 -0
- package/src/navigation/helpers.ts +85 -0
- package/src/navigation/index.tsx +81 -0
- package/src/navigation/types.ts +124 -0
- package/src/navigation/workflowNavigator.ts +131 -0
- package/src/providers/ApiProvider.tsx +43 -0
- package/src/providers/MasterDataProvider.tsx +30 -0
- package/src/screens/AadhaarVerification.tsx +809 -0
- package/src/screens/AddBankAccount.tsx +541 -0
- package/src/screens/BankDetail.tsx +826 -0
- package/src/screens/BookFD.tsx +330 -0
- package/src/screens/Employee.tsx +822 -0
- package/src/screens/FDCalculator.tsx +987 -0
- package/src/screens/FDList.tsx +1284 -0
- package/src/screens/FindIFSC.tsx +332 -0
- package/src/screens/Home.tsx +152 -0
- package/src/screens/NomineeDetail.tsx +800 -0
- package/src/screens/PayNow.tsx +282 -0
- package/src/screens/Payment.tsx +224 -0
- package/src/screens/PaymentStatus.tsx +561 -0
- package/src/screens/ReviewKYC.tsx +956 -0
- package/src/state/paymentSession.ts +13 -0
- package/src/store/fdListSelectedSlice.ts +42 -0
- package/src/store/hooks.ts +27 -0
- package/src/store/index.ts +3 -0
- package/src/store/onboardingSlice.ts +37 -0
- package/src/store/store.ts +35 -0
- package/src/theme/ThemeContext.tsx +82 -0
- package/src/theme/colors.ts +90 -0
- package/src/theme/index.ts +64 -0
- package/src/theme/shadows.ts +61 -0
- package/src/theme/typography.ts +151 -0
- package/src/types/dataTypes.ts +37 -0
- package/src/types/env.d.ts +93 -0
- package/src/types/workflowTypes.ts +12 -0
- package/src/utils/apiLogger.ts +166 -0
- package/src/utils/encryption.ts +159 -0
- package/src/utils/getFDData.ts +175 -0
- package/src/utils/globalData.ts +7 -0
|
@@ -0,0 +1,956 @@
|
|
|
1
|
+
import React, { useState, useEffect, useRef } from 'react';
|
|
2
|
+
import { useRoute } from '@react-navigation/native';
|
|
3
|
+
import { View, Text, StyleSheet, ScrollView, TouchableOpacity, Alert, Image, TouchableWithoutFeedback, Keyboard, TextInput, BackHandler, Platform, ActivityIndicator, KeyboardAvoidingView } from 'react-native';
|
|
4
|
+
import Icon from 'react-native-vector-icons/Ionicons';
|
|
5
|
+
import SafeAreaWrapper from '../components/SafeAreaWrapper';
|
|
6
|
+
import { Header, DropdownSelector } from '../components';
|
|
7
|
+
import ActionButton from '../components/ActionButton';
|
|
8
|
+
import TextFieldWithLabel from '../components/TextFieldWithLabel';
|
|
9
|
+
import { useColors, useTypography, useTheme } from '../theme/ThemeContext';
|
|
10
|
+
import { usePreviousStateMutation } from '../api/workflowApi';
|
|
11
|
+
import { getAppData, getUserInfoForAPI } from '../config/appDataConfig';
|
|
12
|
+
import { useMasterData } from '../providers/MasterDataProvider';
|
|
13
|
+
import { navigate } from '../navigation/helpers';
|
|
14
|
+
import { useAppSelector } from '../store';
|
|
15
|
+
import { useGetCustomerApplicationDetailsMutation } from '../api/customerApi';
|
|
16
|
+
import { usePanRapidNoMutation } from '../api/kycApi';
|
|
17
|
+
import { getGlobalData } from '../utils/globalData';
|
|
18
|
+
import { base64Images } from '../constants/strings/base64Images';
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
export interface ReviewKYCProps {
|
|
22
|
+
onGoBack?: () => void;
|
|
23
|
+
onContinue?: (kycData: KYCData) => void;
|
|
24
|
+
initialData?: Partial<KYCData>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface KYCData {
|
|
28
|
+
panCard: string; // Maps to appData.panNumber (required from main app)
|
|
29
|
+
dateOfBirth: string; // Maps to appData.dob (formatted from YYYY-MM-DD to DD/MM/YYYY for display)
|
|
30
|
+
maritalStatus: string; // Maps to appData.maritalStatus (uses full values like Married/Unmarried/Other)
|
|
31
|
+
area: string; // Maps to appData.area
|
|
32
|
+
city: string; // Maps to appData.city
|
|
33
|
+
addressLine1: string; // Maps to appData.address
|
|
34
|
+
addressLine2: string; // Maps to appData.area (secondary address line)
|
|
35
|
+
pincode: string; // Maps to appData.pinCode
|
|
36
|
+
state: string; // Maps to appData.state
|
|
37
|
+
country: string; // Maps to appData.country
|
|
38
|
+
useExistingAddress: boolean; // User preference for address usage
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const ReviewKYC: React.FC<ReviewKYCProps> = ({
|
|
42
|
+
onGoBack,
|
|
43
|
+
onContinue,
|
|
44
|
+
initialData,
|
|
45
|
+
}) => {
|
|
46
|
+
const route = useRoute<any>();
|
|
47
|
+
const fdDataFromRoute = route?.params?.fdData;
|
|
48
|
+
|
|
49
|
+
// Route params processed
|
|
50
|
+
|
|
51
|
+
const colors = useColors();
|
|
52
|
+
const typography = useTypography();
|
|
53
|
+
const { themeName } = useTheme();
|
|
54
|
+
const styles = createStyles(colors, typography, themeName);
|
|
55
|
+
|
|
56
|
+
// Previous State API
|
|
57
|
+
const [previousState] = usePreviousStateMutation();
|
|
58
|
+
const { masterData, setMasterData } = useMasterData();
|
|
59
|
+
const workflowInstanceId = useAppSelector((state: any) => state?.onboarding?.workflowInstanceId);
|
|
60
|
+
const applicationId = useAppSelector((state: any) => state?.onboarding?.applicationId);
|
|
61
|
+
const customerId = useAppSelector((state: any) => state?.onboarding?.customerId);
|
|
62
|
+
const entityId = useAppSelector((state: any) => state?.onboarding?.entityid);
|
|
63
|
+
const wfStatus = useAppSelector((state: any) => state?.onboarding?.wfStatus);
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
// Customer Application Details API
|
|
67
|
+
const [getCustomerApplicationDetails, {
|
|
68
|
+
data: customerApplicationDetails,
|
|
69
|
+
error: customerApplicationDetailsError,
|
|
70
|
+
isLoading: isLoadingCustomerApplicationDetails,
|
|
71
|
+
}] = useGetCustomerApplicationDetailsMutation();
|
|
72
|
+
|
|
73
|
+
// PAN Rapid API
|
|
74
|
+
const [panRapidNo, {
|
|
75
|
+
data: panRapidResponse,
|
|
76
|
+
error: panRapidError,
|
|
77
|
+
isLoading: isLoadingPanRapid,
|
|
78
|
+
}] = usePanRapidNoMutation();
|
|
79
|
+
|
|
80
|
+
// Get providerId from app data or use default
|
|
81
|
+
const appData = getAppData();
|
|
82
|
+
const defaultProviderId = useAppSelector((state: any) => state?.onboarding?.providerId);// Default Shriram provider ID
|
|
83
|
+
|
|
84
|
+
// Use master data from global state if available
|
|
85
|
+
const effectiveMasterData = masterData;
|
|
86
|
+
|
|
87
|
+
// Call customer application details API on component mount
|
|
88
|
+
React.useEffect(() => {
|
|
89
|
+
|
|
90
|
+
// Ensure IDs are available from onboarding before calling API
|
|
91
|
+
if (!applicationId || !customerId) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Use async/await for better error handling
|
|
96
|
+
const callAPI = async () => {
|
|
97
|
+
try {
|
|
98
|
+
|
|
99
|
+
// Add timeout to detect if API call is hanging
|
|
100
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
101
|
+
setTimeout(() => reject(new Error('API call timeout after 60 seconds')), 60000);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// Get user info from app data
|
|
105
|
+
const userInfo = getUserInfoForAPI();
|
|
106
|
+
|
|
107
|
+
const apiPromise = getCustomerApplicationDetails({
|
|
108
|
+
providerId: defaultProviderId,
|
|
109
|
+
applicationId: applicationId,
|
|
110
|
+
customerId: customerId,
|
|
111
|
+
workflowInstanceId: workflowInstanceId,
|
|
112
|
+
userreferenceid: userInfo.userReferenceId,
|
|
113
|
+
applicationid: applicationId,
|
|
114
|
+
entityid: entityId,
|
|
115
|
+
}).unwrap();
|
|
116
|
+
|
|
117
|
+
const response = await Promise.race([apiPromise, timeoutPromise]);
|
|
118
|
+
} catch (error) {
|
|
119
|
+
// Handle error silently or implement proper error handling
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
callAPI();
|
|
124
|
+
}, [applicationId, customerId, workflowInstanceId]);
|
|
125
|
+
|
|
126
|
+
// Update form data when API response is received
|
|
127
|
+
React.useEffect(() => {
|
|
128
|
+
if (customerApplicationDetails?.data) {
|
|
129
|
+
const apiData = customerApplicationDetails.data;
|
|
130
|
+
|
|
131
|
+
// Check if KYC details exist in the response
|
|
132
|
+
if (apiData.kyc_details && Array.isArray(apiData.kyc_details) && apiData.kyc_details.length > 0) {
|
|
133
|
+
|
|
134
|
+
// Use the first KYC detail (assuming there's one primary KYC record)
|
|
135
|
+
const kycDetail = apiData.kyc_details[0];
|
|
136
|
+
|
|
137
|
+
setKycData(prev => ({
|
|
138
|
+
...prev,
|
|
139
|
+
// Map KYC detail fields to form fields - API data takes priority
|
|
140
|
+
panCard: kycDetail.panNumber || prev.panCard,
|
|
141
|
+
dateOfBirth: kycDetail.dob ? formatDateForDisplay(kycDetail.dob) : prev.dateOfBirth,
|
|
142
|
+
maritalStatus: kycDetail.maritalStatus || prev.maritalStatus,
|
|
143
|
+
area: kycDetail.area || prev.area,
|
|
144
|
+
city: kycDetail.city || prev.city,
|
|
145
|
+
addressLine1: kycDetail.addressLine1 || kycDetail.address || prev.addressLine1,
|
|
146
|
+
addressLine2: kycDetail.addressLine2 || prev.addressLine2,
|
|
147
|
+
pincode: kycDetail.pincode || prev.pincode,
|
|
148
|
+
state: kycDetail.state || prev.state,
|
|
149
|
+
country: kycDetail.country || prev.country,
|
|
150
|
+
}));
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Also check for customer data in the response for marital status and other fields
|
|
154
|
+
if (apiData.customer) {
|
|
155
|
+
setKycData(prev => ({
|
|
156
|
+
...prev,
|
|
157
|
+
maritalStatus: apiData.customer.marital_status || prev.maritalStatus,
|
|
158
|
+
dateOfBirth: apiData.customer.dob ? formatDateForDisplay(apiData.customer.dob) : prev.dateOfBirth,
|
|
159
|
+
}));
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Check for address data in the response
|
|
163
|
+
if (apiData.address) {
|
|
164
|
+
setKycData(prev => ({
|
|
165
|
+
...prev,
|
|
166
|
+
area: apiData.address.area || prev.area,
|
|
167
|
+
city: apiData.address.city || prev.city,
|
|
168
|
+
addressLine1: apiData.address.line1 || prev.addressLine1,
|
|
169
|
+
addressLine2: apiData.address.line2 || prev.addressLine2,
|
|
170
|
+
pincode: apiData.address.pincode || prev.pincode,
|
|
171
|
+
state: apiData.address.state || prev.state,
|
|
172
|
+
country: apiData.address.country || prev.country,
|
|
173
|
+
}));
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}, [customerApplicationDetails]);
|
|
177
|
+
|
|
178
|
+
// App data is already loaded above
|
|
179
|
+
|
|
180
|
+
// Helper function to format date from YYYY-MM-DD to DD/MM/YYYY
|
|
181
|
+
const formatDateForDisplay = (dateString: string): string => {
|
|
182
|
+
if (!dateString) return '';
|
|
183
|
+
const [year, month, day] = dateString.split('-');
|
|
184
|
+
return `${day}/${month}/${year}`;
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
// Marital status is now used as-is from API without any code conversion
|
|
188
|
+
|
|
189
|
+
// App data processed
|
|
190
|
+
|
|
191
|
+
// API status monitoring complete
|
|
192
|
+
|
|
193
|
+
const [kycData, setKycData] = useState<KYCData>({
|
|
194
|
+
panCard: initialData?.panCard || appData?.panNumber || '',
|
|
195
|
+
dateOfBirth: initialData?.dateOfBirth || formatDateForDisplay(appData?.dob || ''),
|
|
196
|
+
maritalStatus: initialData?.maritalStatus || appData?.maritalStatus || '', // Keep empty to show placeholder
|
|
197
|
+
area: initialData?.area || appData?.area || '',
|
|
198
|
+
city: initialData?.city || appData?.city || '',
|
|
199
|
+
addressLine1: initialData?.addressLine1 || appData?.address || '',
|
|
200
|
+
addressLine2: initialData?.addressLine2 || appData?.area || '', // Use area as address line 2
|
|
201
|
+
pincode: initialData?.pincode || appData?.pinCode || '',
|
|
202
|
+
state: initialData?.state || appData?.state || '',
|
|
203
|
+
country: initialData?.country || appData?.country || '',
|
|
204
|
+
useExistingAddress: initialData?.useExistingAddress ?? false,
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
// Error states for each field
|
|
208
|
+
const [fieldErrors, setFieldErrors] = useState<{ [key: string]: string }>({});
|
|
209
|
+
|
|
210
|
+
// State to track marital status dropdown visibility
|
|
211
|
+
const [showMaritalStatusMenu, setShowMaritalStatusMenu] = useState(false);
|
|
212
|
+
const [isGoingBack, setIsGoingBack] = useState(false);
|
|
213
|
+
|
|
214
|
+
// Ref to track currently focused text input
|
|
215
|
+
const focusedTextInputRef = useRef<TextInput | null>(null);
|
|
216
|
+
|
|
217
|
+
// Refs for each text field
|
|
218
|
+
const panCardRef = useRef<TextInput>(null);
|
|
219
|
+
const dateOfBirthRef = useRef<TextInput>(null);
|
|
220
|
+
const areaRef = useRef<TextInput>(null);
|
|
221
|
+
const cityRef = useRef<TextInput>(null);
|
|
222
|
+
const addressLine1Ref = useRef<TextInput>(null);
|
|
223
|
+
const addressLine2Ref = useRef<TextInput>(null);
|
|
224
|
+
const pincodeRef = useRef<TextInput>(null);
|
|
225
|
+
const stateRef = useRef<TextInput>(null);
|
|
226
|
+
const countryRef = useRef<TextInput>(null);
|
|
227
|
+
|
|
228
|
+
// Function to close dropdown when other fields are focused
|
|
229
|
+
const closeDropdown = () => {
|
|
230
|
+
setShowMaritalStatusMenu(false);
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
// Function to blur currently focused text input
|
|
234
|
+
const blurFocusedTextInput = () => {
|
|
235
|
+
if (focusedTextInputRef.current) {
|
|
236
|
+
focusedTextInputRef.current.blur();
|
|
237
|
+
focusedTextInputRef.current = null;
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
// Function to handle focus and store ref
|
|
242
|
+
const handleFieldFocus = (ref: React.RefObject<TextInput>) => {
|
|
243
|
+
// Blur any previously focused field
|
|
244
|
+
blurFocusedTextInput();
|
|
245
|
+
// Store the new focused field ref
|
|
246
|
+
focusedTextInputRef.current = ref.current;
|
|
247
|
+
// Close dropdown
|
|
248
|
+
closeDropdown();
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
// Add keyboard event listener to close dropdown when keyboard appears
|
|
252
|
+
useEffect(() => {
|
|
253
|
+
const keyboardDidShowListener = Keyboard.addListener('keyboardDidShow', () => {
|
|
254
|
+
closeDropdown();
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
return () => {
|
|
258
|
+
keyboardDidShowListener?.remove();
|
|
259
|
+
};
|
|
260
|
+
}, []);
|
|
261
|
+
|
|
262
|
+
// Get marital status options from master data with fallback
|
|
263
|
+
const maritalStatusOptions: string[] = React.useMemo(() => {
|
|
264
|
+
try {
|
|
265
|
+
const src = (effectiveMasterData as any)?.data || effectiveMasterData || {};
|
|
266
|
+
const raw = src?.maritalStatusOptions ||
|
|
267
|
+
src?.maritalStatusList ||
|
|
268
|
+
src?.maritalStatus ||
|
|
269
|
+
src?.maritalOptions;
|
|
270
|
+
|
|
271
|
+
if (Array.isArray(raw) && raw.length) {
|
|
272
|
+
return raw.map((v: any) => String(v)).filter(Boolean);
|
|
273
|
+
}
|
|
274
|
+
if (typeof raw === 'string' && raw.trim().length) {
|
|
275
|
+
return raw.split(',').map((v: string) => v.trim()).filter(Boolean);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Fallback defaults
|
|
279
|
+
return ['Married', 'Unmarried', 'Other'];
|
|
280
|
+
} catch (e) {
|
|
281
|
+
return ['Married', 'Unmarried', 'Other'];
|
|
282
|
+
}
|
|
283
|
+
}, [effectiveMasterData]);
|
|
284
|
+
|
|
285
|
+
// Do not auto-select a default marital status; keep empty to show placeholder when not provided by main app or API
|
|
286
|
+
|
|
287
|
+
// Validate individual field and set error
|
|
288
|
+
const validateField = (field: keyof KYCData, value: string): string => {
|
|
289
|
+
if (!value || value.trim().length === 0) {
|
|
290
|
+
// Use "District" for area field
|
|
291
|
+
if (field === 'area') {
|
|
292
|
+
return 'District is required';
|
|
293
|
+
}
|
|
294
|
+
return `${field.charAt(0).toUpperCase() + field.slice(1)} is required`;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Minimum 2 characters validation for text fields (excluding PAN, pincode, dateOfBirth, maritalStatus)
|
|
298
|
+
const textFields = ['area', 'city', 'addressLine1', 'addressLine2', 'state', 'country'];
|
|
299
|
+
if (textFields.includes(field)) {
|
|
300
|
+
if (value.trim().length < 2) {
|
|
301
|
+
// Use "Address" for addressLine1 and addressLine2
|
|
302
|
+
if (field === 'addressLine1' || field === 'addressLine2') {
|
|
303
|
+
return 'Address must be at least 2 characters';
|
|
304
|
+
} else if (field === 'area') {
|
|
305
|
+
return 'District must be at least 2 characters';
|
|
306
|
+
}
|
|
307
|
+
return `${field.charAt(0).toUpperCase() + field.slice(1)} must be at least 2 characters`;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Special validation for PAN Card
|
|
312
|
+
if (field === 'panCard') {
|
|
313
|
+
const panRegex = /^[A-Z]{5}[0-9]{4}[A-Z]{1}$/;
|
|
314
|
+
const upperValue = value.toUpperCase();
|
|
315
|
+
if (!panRegex.test(upperValue)) {
|
|
316
|
+
return 'Please enter a valid PAN (e.g., ABCDE1234F)';
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Special validation for pincode
|
|
321
|
+
if (field === 'pincode') {
|
|
322
|
+
if (!validatePincode(value)) {
|
|
323
|
+
return 'Please enter a valid 6-digit pincode (should not start with 0)';
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
return '';
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
// Render error message for a field
|
|
331
|
+
const renderFieldError = (field: keyof KYCData) => {
|
|
332
|
+
const error = fieldErrors[field];
|
|
333
|
+
if (!error) return null;
|
|
334
|
+
|
|
335
|
+
return (
|
|
336
|
+
<View style={styles.errorContainer}>
|
|
337
|
+
{Platform.OS === 'android' && (
|
|
338
|
+
<Icon name="warning" size={16} color={colors.error || '#FF0000'} style={styles.errorIcon} />
|
|
339
|
+
)}
|
|
340
|
+
<Text style={styles.errorText}>{error}</Text>
|
|
341
|
+
</View>
|
|
342
|
+
);
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
// Enhanced field validation functions
|
|
346
|
+
const validateAndCleanText = (text: string, field: keyof KYCData): string => {
|
|
347
|
+
let cleanedText = text;
|
|
348
|
+
|
|
349
|
+
// Apply field-specific validation
|
|
350
|
+
switch (field) {
|
|
351
|
+
case 'panCard':
|
|
352
|
+
// Only allow alphanumeric, limit to 10 characters
|
|
353
|
+
// Don't uppercase here to prevent cursor jumping - let autoCapitalize handle display
|
|
354
|
+
cleanedText = cleanedText.replace(/[^a-zA-Z0-9]/g, '').substring(0, 10);
|
|
355
|
+
break;
|
|
356
|
+
case 'area':
|
|
357
|
+
// Allow only letters, numbers, spaces; collapse spaces; limit length
|
|
358
|
+
cleanedText = cleanedText.replace(/[^a-zA-Z0-9 ]/g, '').replace(/\s+/g, ' ').substring(0, 50);
|
|
359
|
+
break;
|
|
360
|
+
case 'city':
|
|
361
|
+
// Allow only letters, numbers, spaces; collapse spaces; limit length
|
|
362
|
+
cleanedText = cleanedText.replace(/[^a-zA-Z0-9 ]/g, '').replace(/\s+/g, ' ').substring(0, 30);
|
|
363
|
+
break;
|
|
364
|
+
case 'addressLine1':
|
|
365
|
+
case 'addressLine2':
|
|
366
|
+
// Allow letters, numbers, spaces, and common address special characters (comma, period, hyphen, slash, hash, parentheses, apostrophe, quotes)
|
|
367
|
+
// Collapse multiple spaces to single space; limit length
|
|
368
|
+
cleanedText = cleanedText.replace(/[^a-zA-Z0-9 ,./()#'"-]/g, '').replace(/\s+/g, ' ').substring(0, 100);
|
|
369
|
+
break;
|
|
370
|
+
case 'pincode':
|
|
371
|
+
// Only allow digits and limit to 6 characters
|
|
372
|
+
cleanedText = cleanedText.replace(/[^0-9]/g, '').substring(0, 6);
|
|
373
|
+
break;
|
|
374
|
+
case 'state':
|
|
375
|
+
// Allow only letters, numbers, spaces; collapse spaces; limit length
|
|
376
|
+
cleanedText = cleanedText.replace(/[^a-zA-Z0-9 ]/g, '').replace(/\s+/g, ' ').substring(0, 40);
|
|
377
|
+
break;
|
|
378
|
+
case 'country':
|
|
379
|
+
// Allow only letters, numbers, spaces; collapse spaces; limit length
|
|
380
|
+
cleanedText = cleanedText.replace(/[^a-zA-Z0-9 ]/g, '').replace(/\s+/g, ' ').substring(0, 40);
|
|
381
|
+
break;
|
|
382
|
+
default:
|
|
383
|
+
// Default: allow only letters, numbers, spaces
|
|
384
|
+
cleanedText = cleanedText.replace(/[^a-zA-Z0-9 ]/g, '').replace(/\s+/g, ' ');
|
|
385
|
+
break;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
return cleanedText;
|
|
389
|
+
};
|
|
390
|
+
|
|
391
|
+
const validatePincode = (pincode: string): boolean => {
|
|
392
|
+
// Must be exactly 6 digits
|
|
393
|
+
if (pincode.length !== 6) return false;
|
|
394
|
+
|
|
395
|
+
// Must be all digits
|
|
396
|
+
if (!/^\d{6}$/.test(pincode)) return false;
|
|
397
|
+
|
|
398
|
+
// Basic pincode validation (first digit should be 1-9, not 0)
|
|
399
|
+
if (pincode.startsWith('0')) return false;
|
|
400
|
+
|
|
401
|
+
return true;
|
|
402
|
+
};
|
|
403
|
+
|
|
404
|
+
const updateField = (field: keyof KYCData, value: string | boolean) => {
|
|
405
|
+
|
|
406
|
+
// Apply validation and cleaning for text fields
|
|
407
|
+
let processedValue = value;
|
|
408
|
+
if (typeof value === 'string' && field !== 'useExistingAddress') {
|
|
409
|
+
processedValue = validateAndCleanText(value, field);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
setKycData(prev => {
|
|
413
|
+
const newData = {
|
|
414
|
+
...prev,
|
|
415
|
+
[field]: processedValue
|
|
416
|
+
};
|
|
417
|
+
|
|
418
|
+
// Log checkbox state changes
|
|
419
|
+
if (field === 'useExistingAddress') {
|
|
420
|
+
// Checkbox state changed
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
return newData;
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
// Validate field and set error
|
|
427
|
+
if (typeof processedValue === 'string') {
|
|
428
|
+
const error = validateField(field, processedValue);
|
|
429
|
+
setFieldErrors(prev => ({
|
|
430
|
+
...prev,
|
|
431
|
+
[field]: error,
|
|
432
|
+
}));
|
|
433
|
+
}
|
|
434
|
+
};
|
|
435
|
+
|
|
436
|
+
// Helper function to format date from DD/MM/YYYY back to YYYY-MM-DD
|
|
437
|
+
const formatDateForAPI = (dateString: string): string => {
|
|
438
|
+
if (!dateString) return '';
|
|
439
|
+
const [day, month, year] = dateString.split('/');
|
|
440
|
+
return `${year}-${month}-${day}`;
|
|
441
|
+
};
|
|
442
|
+
|
|
443
|
+
// Pass-through marital status: use the exact value from master data (no S/M/D mapping)
|
|
444
|
+
const getMaritalStatusCode = (display: string): string => {
|
|
445
|
+
return display || '';
|
|
446
|
+
};
|
|
447
|
+
|
|
448
|
+
// Validation function to check if all required fields are filled and valid
|
|
449
|
+
const validateForm = (): boolean => {
|
|
450
|
+
const requiredFields = [
|
|
451
|
+
kycData.panCard,
|
|
452
|
+
kycData.dateOfBirth,
|
|
453
|
+
kycData.maritalStatus,
|
|
454
|
+
kycData.area,
|
|
455
|
+
kycData.city,
|
|
456
|
+
kycData.addressLine1,
|
|
457
|
+
kycData.pincode,
|
|
458
|
+
kycData.state,
|
|
459
|
+
kycData.country,
|
|
460
|
+
];
|
|
461
|
+
|
|
462
|
+
// Check if all required fields are filled
|
|
463
|
+
const allFieldsFilled = requiredFields.every(field => field && field.trim().length > 0);
|
|
464
|
+
|
|
465
|
+
// Check minimum 2 characters for text fields (only if field is filled)
|
|
466
|
+
const textFields = {
|
|
467
|
+
area: kycData.area,
|
|
468
|
+
city: kycData.city,
|
|
469
|
+
addressLine1: kycData.addressLine1,
|
|
470
|
+
state: kycData.state,
|
|
471
|
+
country: kycData.country,
|
|
472
|
+
};
|
|
473
|
+
const allTextFieldsValid = Object.values(textFields).every(field => {
|
|
474
|
+
// If field is empty, it's already checked by allFieldsFilled
|
|
475
|
+
// If field has value, it must be at least 2 characters
|
|
476
|
+
return !field || field.trim().length === 0 || field.trim().length >= 2;
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
// Additional validation for pincode
|
|
480
|
+
const isPincodeValid = validatePincode(kycData.pincode);
|
|
481
|
+
|
|
482
|
+
return allFieldsFilled && allTextFieldsValid && isPincodeValid;
|
|
483
|
+
};
|
|
484
|
+
|
|
485
|
+
const handleContinue = async () => {
|
|
486
|
+
try {
|
|
487
|
+
|
|
488
|
+
// Validate all required fields
|
|
489
|
+
if (!validateForm()) {
|
|
490
|
+
// Check specifically for pincode validation
|
|
491
|
+
if (!validatePincode(kycData.pincode)) {
|
|
492
|
+
Alert.alert('Invalid Pincode', 'Please enter a valid 6-digit pincode (should not start with 0).');
|
|
493
|
+
} else {
|
|
494
|
+
Alert.alert('Required Fields', 'Please fill in all required fields before continuing.');
|
|
495
|
+
}
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// Convert the display format back to API format before sending
|
|
500
|
+
const apiFormattedData = {
|
|
501
|
+
...kycData,
|
|
502
|
+
dateOfBirth: formatDateForAPI(kycData.dateOfBirth),
|
|
503
|
+
maritalStatus: getMaritalStatusCode(kycData.maritalStatus),
|
|
504
|
+
};
|
|
505
|
+
|
|
506
|
+
|
|
507
|
+
// Get user info from app data
|
|
508
|
+
const userInfo = getUserInfoForAPI();
|
|
509
|
+
|
|
510
|
+
// Call PAN rapid API (body fields from text fields/dropdowns)
|
|
511
|
+
const panRapidRequest = {
|
|
512
|
+
// headers via panApi: provider/workflowInstanceId/userreferenceid/applicationid
|
|
513
|
+
providerId: defaultProviderId,
|
|
514
|
+
workflowInstanceId, // from store
|
|
515
|
+
userreferenceid: userInfo.userReferenceId,
|
|
516
|
+
applicationid: applicationId,
|
|
517
|
+
|
|
518
|
+
// required body params
|
|
519
|
+
customerId: customerId,
|
|
520
|
+
applicationId: applicationId,
|
|
521
|
+
maritalStatus: getMaritalStatusCode(kycData.maritalStatus),
|
|
522
|
+
area: kycData.area,
|
|
523
|
+
addressLine1: kycData.addressLine1,
|
|
524
|
+
addressLine2: kycData.addressLine2, // Added missing addressLine2 field
|
|
525
|
+
city: kycData.city,
|
|
526
|
+
state: kycData.state,
|
|
527
|
+
country: kycData.country,
|
|
528
|
+
pincode: kycData.pincode,
|
|
529
|
+
panNo: kycData.panCard.toUpperCase(), // Uppercase PAN before sending to API
|
|
530
|
+
mobileNo: userInfo.mobileNumber,
|
|
531
|
+
addressConsentGiven: !!kycData.useExistingAddress,
|
|
532
|
+
};
|
|
533
|
+
|
|
534
|
+
|
|
535
|
+
const panResponse = await panRapidNo(panRapidRequest).unwrap();
|
|
536
|
+
|
|
537
|
+
// Normalize payload (API returns data as array of one element)
|
|
538
|
+
const payload: any = Array.isArray((panResponse as any)?.data)
|
|
539
|
+
? (panResponse as any).data[0]
|
|
540
|
+
: (panResponse as any)?.data ?? (panResponse as any);
|
|
541
|
+
|
|
542
|
+
// Extract fields with tolerant casing/locations
|
|
543
|
+
const panRapidNumber = payload?.panRapidNumber ?? payload?.panrapidnumber ?? (panResponse as any)?.panRapidNumber ?? (panResponse as any)?.panrapidnumber ?? null;
|
|
544
|
+
|
|
545
|
+
// Check demographicInfoSaved gate
|
|
546
|
+
const demographicInfoSaved = payload?.demographicInfoSaved ?? (panResponse as any)?.demographicInfoSaved;
|
|
547
|
+
|
|
548
|
+
if (!demographicInfoSaved) {
|
|
549
|
+
Alert.alert('Action required', 'Your demographic information could not be saved. Please review the details and try again.');
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
552
|
+
const data = getGlobalData();
|
|
553
|
+
// Decide next screen based on panrapidnumber existence
|
|
554
|
+
if (panRapidNumber && data.completeFDData) {
|
|
555
|
+
navigate('PayNow', { fdData: fdDataFromRoute });
|
|
556
|
+
} else if (panRapidNumber) {
|
|
557
|
+
navigate('Employee');
|
|
558
|
+
} else {
|
|
559
|
+
navigate('AadhaarVerification', { fdData: fdDataFromRoute });
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
// Do not trigger onContinue here; navigation is handled above
|
|
563
|
+
|
|
564
|
+
} catch (error) {
|
|
565
|
+
// Show error message to user
|
|
566
|
+
Alert.alert(
|
|
567
|
+
'PAN Verification Failed',
|
|
568
|
+
'Unable to verify your PAN details. Please check your information and try again.',
|
|
569
|
+
[{ text: 'OK' }]
|
|
570
|
+
);
|
|
571
|
+
|
|
572
|
+
// Do not navigate on failure - stay on current screen
|
|
573
|
+
// User can retry or correct their information
|
|
574
|
+
}
|
|
575
|
+
};
|
|
576
|
+
|
|
577
|
+
const renderInputField = (
|
|
578
|
+
label: string,
|
|
579
|
+
value: string,
|
|
580
|
+
field: keyof KYCData,
|
|
581
|
+
placeholder?: string,
|
|
582
|
+
editable: boolean = true,
|
|
583
|
+
variant: 'text' | 'numeric' | 'email' = 'text',
|
|
584
|
+
maxLength?: number,
|
|
585
|
+
textInputRef?: React.RefObject<TextInput>
|
|
586
|
+
) => (
|
|
587
|
+
<View>
|
|
588
|
+
<TextFieldWithLabel
|
|
589
|
+
label={label}
|
|
590
|
+
value={value}
|
|
591
|
+
onChangeText={(text) => updateField(field, text)}
|
|
592
|
+
placeholder={placeholder}
|
|
593
|
+
editable={editable}
|
|
594
|
+
variant={variant}
|
|
595
|
+
maxLength={maxLength}
|
|
596
|
+
keyboardType={variant === 'numeric' ? 'numeric' : variant === 'email' ? 'email-address' : 'default'}
|
|
597
|
+
autoCapitalize={variant === 'email' ? 'none' : 'words'}
|
|
598
|
+
onFocus={() => textInputRef && handleFieldFocus(textInputRef)}
|
|
599
|
+
textInputRef={textInputRef}
|
|
600
|
+
returnKeyType="done"
|
|
601
|
+
onSubmitEditing={() => {
|
|
602
|
+
if (textInputRef?.current) {
|
|
603
|
+
textInputRef.current.blur();
|
|
604
|
+
}
|
|
605
|
+
Keyboard.dismiss();
|
|
606
|
+
}}
|
|
607
|
+
/>
|
|
608
|
+
{renderFieldError(field)}
|
|
609
|
+
</View>
|
|
610
|
+
);
|
|
611
|
+
|
|
612
|
+
const renderMaritalStatusDropdown = () => {
|
|
613
|
+
return (
|
|
614
|
+
<View>
|
|
615
|
+
<TextFieldWithLabel
|
|
616
|
+
label="Marital Status"
|
|
617
|
+
value={kycData.maritalStatus}
|
|
618
|
+
onChangeText={(text) => updateField('maritalStatus', text)}
|
|
619
|
+
variant="dropdown"
|
|
620
|
+
options={maritalStatusOptions}
|
|
621
|
+
isDropdownOpen={showMaritalStatusMenu}
|
|
622
|
+
onDropdownToggle={() => {
|
|
623
|
+
// Blur any focused text field when dropdown is opened
|
|
624
|
+
blurFocusedTextInput();
|
|
625
|
+
setShowMaritalStatusMenu(prev => !prev);
|
|
626
|
+
}}
|
|
627
|
+
onDropdownSelect={(option) => {
|
|
628
|
+
setShowMaritalStatusMenu(false);
|
|
629
|
+
updateField('maritalStatus', option);
|
|
630
|
+
}}
|
|
631
|
+
placeholder="Select Marital Status"
|
|
632
|
+
editable={true}
|
|
633
|
+
/>
|
|
634
|
+
{showMaritalStatusMenu && (
|
|
635
|
+
<View style={styles.inlineMenu}>
|
|
636
|
+
{maritalStatusOptions.map((option: string) => (
|
|
637
|
+
<TouchableOpacity
|
|
638
|
+
key={option}
|
|
639
|
+
style={styles.modalOption}
|
|
640
|
+
onPress={() => {
|
|
641
|
+
setShowMaritalStatusMenu(false);
|
|
642
|
+
updateField('maritalStatus', option);
|
|
643
|
+
}}
|
|
644
|
+
>
|
|
645
|
+
<Text style={styles.modalOptionText}>{option}</Text>
|
|
646
|
+
</TouchableOpacity>
|
|
647
|
+
))}
|
|
648
|
+
</View>
|
|
649
|
+
)}
|
|
650
|
+
{renderFieldError('maritalStatus')}
|
|
651
|
+
</View>
|
|
652
|
+
);
|
|
653
|
+
};
|
|
654
|
+
|
|
655
|
+
const renderDateField = (
|
|
656
|
+
label: string,
|
|
657
|
+
value: string,
|
|
658
|
+
field: keyof KYCData,
|
|
659
|
+
editable: boolean = true,
|
|
660
|
+
textInputRef?: React.RefObject<TextInput>
|
|
661
|
+
) => (
|
|
662
|
+
<View>
|
|
663
|
+
<TextFieldWithLabel
|
|
664
|
+
label={label}
|
|
665
|
+
value={value}
|
|
666
|
+
onChangeText={(text) => updateField(field, text)}
|
|
667
|
+
variant="date"
|
|
668
|
+
placeholder="DD/MM/YYYY"
|
|
669
|
+
maxLength={10}
|
|
670
|
+
dateFormat="DD/MM/YYYY"
|
|
671
|
+
editable={editable}
|
|
672
|
+
onFocus={() => textInputRef && handleFieldFocus(textInputRef)}
|
|
673
|
+
textInputRef={textInputRef}
|
|
674
|
+
onDatePress={() => {
|
|
675
|
+
// TODO: Implement date picker if needed
|
|
676
|
+
}}
|
|
677
|
+
returnKeyType="done"
|
|
678
|
+
onSubmitEditing={() => {
|
|
679
|
+
if (textInputRef?.current) {
|
|
680
|
+
textInputRef.current.blur();
|
|
681
|
+
}
|
|
682
|
+
Keyboard.dismiss();
|
|
683
|
+
}}
|
|
684
|
+
/>
|
|
685
|
+
{renderFieldError(field)}
|
|
686
|
+
</View>
|
|
687
|
+
);
|
|
688
|
+
|
|
689
|
+
// Handler for back button (used by both header and hardware back button)
|
|
690
|
+
const handleBackPress = async () => {
|
|
691
|
+
setIsGoingBack(true);
|
|
692
|
+
try {
|
|
693
|
+
const userInfo = getUserInfoForAPI();
|
|
694
|
+
await previousState({
|
|
695
|
+
providerId: defaultProviderId,
|
|
696
|
+
workflowInstanceId,
|
|
697
|
+
userreferenceid: userInfo.userReferenceId,
|
|
698
|
+
applicationid: applicationId,
|
|
699
|
+
entityid: entityId,
|
|
700
|
+
});
|
|
701
|
+
} catch (e) {
|
|
702
|
+
// Handle error silently
|
|
703
|
+
} finally {
|
|
704
|
+
setIsGoingBack(false);
|
|
705
|
+
//onGoBack?.();
|
|
706
|
+
navigate('FDCalculator');
|
|
707
|
+
}
|
|
708
|
+
};
|
|
709
|
+
|
|
710
|
+
// Handle Android hardware back button
|
|
711
|
+
useEffect(() => {
|
|
712
|
+
if (Platform.OS !== 'android') return;
|
|
713
|
+
|
|
714
|
+
const onHardwareBackPress = () => {
|
|
715
|
+
handleBackPress();
|
|
716
|
+
return true; // Prevent default behavior
|
|
717
|
+
};
|
|
718
|
+
|
|
719
|
+
const backHandler = BackHandler.addEventListener(
|
|
720
|
+
'hardwareBackPress',
|
|
721
|
+
onHardwareBackPress
|
|
722
|
+
);
|
|
723
|
+
|
|
724
|
+
return () => backHandler.remove();
|
|
725
|
+
}, [defaultProviderId, workflowInstanceId, applicationId, entityId]);
|
|
726
|
+
|
|
727
|
+
return (
|
|
728
|
+
<SafeAreaWrapper
|
|
729
|
+
includeTop={false}
|
|
730
|
+
bottomPadding={25}
|
|
731
|
+
statusBarColor="#000000"
|
|
732
|
+
statusBarStyle="light-content"
|
|
733
|
+
>
|
|
734
|
+
<Header
|
|
735
|
+
title="Review KYC"
|
|
736
|
+
onBackPress={handleBackPress}
|
|
737
|
+
backgroundColor={colors.primary}
|
|
738
|
+
/>
|
|
739
|
+
|
|
740
|
+
<KeyboardAvoidingView
|
|
741
|
+
behavior={Platform.OS === 'ios' ? 'padding' : undefined}
|
|
742
|
+
style={styles.keyboardAvoidingView}
|
|
743
|
+
keyboardVerticalOffset={Platform.OS === 'ios' ? 0 : 0}
|
|
744
|
+
>
|
|
745
|
+
<ScrollView
|
|
746
|
+
style={styles.container}
|
|
747
|
+
showsVerticalScrollIndicator={false}
|
|
748
|
+
scrollEnabled={!isLoadingPanRapid}
|
|
749
|
+
keyboardShouldPersistTaps="handled"
|
|
750
|
+
contentContainerStyle={styles.scrollContent}
|
|
751
|
+
>
|
|
752
|
+
<TouchableWithoutFeedback onPress={isLoadingPanRapid ? undefined : closeDropdown}>
|
|
753
|
+
<View>
|
|
754
|
+
<View>
|
|
755
|
+
<TextFieldWithLabel
|
|
756
|
+
label="PAN Card"
|
|
757
|
+
value={kycData.panCard}
|
|
758
|
+
onChangeText={(text) => updateField('panCard', text)}
|
|
759
|
+
placeholder="Enter PAN Card"
|
|
760
|
+
editable={false}
|
|
761
|
+
variant="text"
|
|
762
|
+
maxLength={10}
|
|
763
|
+
keyboardType="default"
|
|
764
|
+
autoCapitalize="characters"
|
|
765
|
+
onFocus={() => handleFieldFocus(panCardRef)}
|
|
766
|
+
textInputRef={panCardRef}
|
|
767
|
+
returnKeyType="done"
|
|
768
|
+
onSubmitEditing={() => {
|
|
769
|
+
if (panCardRef?.current) {
|
|
770
|
+
panCardRef.current.blur();
|
|
771
|
+
}
|
|
772
|
+
Keyboard.dismiss();
|
|
773
|
+
}}
|
|
774
|
+
/>
|
|
775
|
+
{renderFieldError('panCard')}
|
|
776
|
+
</View>
|
|
777
|
+
|
|
778
|
+
{renderDateField('Date of Birth', kycData.dateOfBirth, 'dateOfBirth', false, dateOfBirthRef)}
|
|
779
|
+
|
|
780
|
+
{renderMaritalStatusDropdown()}
|
|
781
|
+
|
|
782
|
+
{renderInputField('District', kycData.area, 'area', 'Enter District', true, 'text', 50, areaRef)}
|
|
783
|
+
|
|
784
|
+
{renderInputField('City', kycData.city, 'city', 'Enter City', true, 'text', 30, cityRef)}
|
|
785
|
+
|
|
786
|
+
{renderInputField('Address line 1', kycData.addressLine1, 'addressLine1', 'Enter Address Line 1', true, 'text', 100, addressLine1Ref)}
|
|
787
|
+
|
|
788
|
+
{renderInputField('Address line 2', kycData.addressLine2, 'addressLine2', 'Enter Address Line 2', true, 'text', 100, addressLine2Ref)}
|
|
789
|
+
|
|
790
|
+
{renderInputField('Pincode', kycData.pincode, 'pincode', 'Enter Pincode', true, 'numeric', 6, pincodeRef)}
|
|
791
|
+
|
|
792
|
+
{renderInputField('State', kycData.state, 'state', 'Enter State', true, 'text', undefined, stateRef)}
|
|
793
|
+
|
|
794
|
+
{renderInputField('Country', kycData.country, 'country', 'Enter Country', true, 'text', undefined, countryRef)}
|
|
795
|
+
|
|
796
|
+
<View style={styles.checkboxContainer}>
|
|
797
|
+
<TouchableOpacity
|
|
798
|
+
style={styles.checkbox}
|
|
799
|
+
onPress={() => updateField('useExistingAddress', !kycData.useExistingAddress)}
|
|
800
|
+
>
|
|
801
|
+
<View style={[
|
|
802
|
+
styles.checkboxBox,
|
|
803
|
+
kycData.useExistingAddress && styles.checkboxChecked
|
|
804
|
+
]}>
|
|
805
|
+
{kycData.useExistingAddress ? (
|
|
806
|
+
<Image
|
|
807
|
+
source={{ uri: (themeName === 'dark') ? base64Images.checkBoxDark : base64Images.filledCheckBox }}
|
|
808
|
+
resizeMode="cover"
|
|
809
|
+
width={20}
|
|
810
|
+
height={20}
|
|
811
|
+
/>
|
|
812
|
+
) : (
|
|
813
|
+
(themeName === 'dark') ? (
|
|
814
|
+
<Image
|
|
815
|
+
source={{ uri: base64Images.unCheckBoxDark }}
|
|
816
|
+
resizeMode="cover"
|
|
817
|
+
width={20}
|
|
818
|
+
height={20}
|
|
819
|
+
/>
|
|
820
|
+
) : null
|
|
821
|
+
)}
|
|
822
|
+
</View>
|
|
823
|
+
</TouchableOpacity>
|
|
824
|
+
<Text style={styles.checkboxText}>
|
|
825
|
+
For existing customers, we'll use the address from our records. For new customers, the address from your Aadhar card will be used. To update, please raise a service request with valid address proof.
|
|
826
|
+
</Text>
|
|
827
|
+
</View>
|
|
828
|
+
|
|
829
|
+
|
|
830
|
+
</View>
|
|
831
|
+
</TouchableWithoutFeedback>
|
|
832
|
+
</ScrollView>
|
|
833
|
+
</KeyboardAvoidingView>
|
|
834
|
+
<ActionButton
|
|
835
|
+
title="Continue"
|
|
836
|
+
onPress={handleContinue}
|
|
837
|
+
disabled={!kycData.useExistingAddress || isLoadingPanRapid || !validateForm()}
|
|
838
|
+
loading={isLoadingPanRapid}
|
|
839
|
+
/>
|
|
840
|
+
{/* Overlay to disable screen interactions during API call */}
|
|
841
|
+
{isLoadingPanRapid && (
|
|
842
|
+
<View style={styles.loadingOverlay} pointerEvents="auto">
|
|
843
|
+
</View>
|
|
844
|
+
)}
|
|
845
|
+
{/* Loading overlay for back navigation */}
|
|
846
|
+
{isGoingBack && (
|
|
847
|
+
<View style={styles.loadingOverlay} pointerEvents="auto">
|
|
848
|
+
<ActivityIndicator size="large" color={colors.primary} />
|
|
849
|
+
</View>
|
|
850
|
+
)}
|
|
851
|
+
</SafeAreaWrapper>
|
|
852
|
+
);
|
|
853
|
+
};
|
|
854
|
+
|
|
855
|
+
const createStyles = (colors: any, typography: any, themeName?: string) => StyleSheet.create({
|
|
856
|
+
keyboardAvoidingView: {
|
|
857
|
+
flex: 1,
|
|
858
|
+
},
|
|
859
|
+
container: {
|
|
860
|
+
flex: 1,
|
|
861
|
+
paddingHorizontal: 16,
|
|
862
|
+
paddingTop: 20,
|
|
863
|
+
},
|
|
864
|
+
scrollContent: {
|
|
865
|
+
flexGrow: 1,
|
|
866
|
+
},
|
|
867
|
+
checkboxContainer: {
|
|
868
|
+
flexDirection: 'row',
|
|
869
|
+
alignItems: 'flex-start',
|
|
870
|
+
marginBottom: 40,
|
|
871
|
+
paddingHorizontal: 4,
|
|
872
|
+
marginTop: 16,
|
|
873
|
+
},
|
|
874
|
+
checkbox: {
|
|
875
|
+
marginRight: 12,
|
|
876
|
+
marginTop: 2,
|
|
877
|
+
},
|
|
878
|
+
checkboxBox: {
|
|
879
|
+
width: 20,
|
|
880
|
+
height: 20,
|
|
881
|
+
borderWidth: 2,
|
|
882
|
+
borderColor: themeName === 'dark' ? colors.tabSelected : colors.primary,
|
|
883
|
+
borderRadius: 4,
|
|
884
|
+
alignItems: 'center',
|
|
885
|
+
justifyContent: 'center',
|
|
886
|
+
backgroundColor: 'white',
|
|
887
|
+
},
|
|
888
|
+
checkboxChecked: {
|
|
889
|
+
// backgroundColor: colors.primary,
|
|
890
|
+
},
|
|
891
|
+
checkboxText: {
|
|
892
|
+
...typography.styles.bodySmall,
|
|
893
|
+
color: colors.textLight,
|
|
894
|
+
flex: 1,
|
|
895
|
+
lineHeight: 18,
|
|
896
|
+
},
|
|
897
|
+
inlineMenu: {
|
|
898
|
+
backgroundColor: themeName === 'dark' ? colors.inputBackground : colors.background,
|
|
899
|
+
borderWidth: themeName === 'dark' ? 1 : 0.5,
|
|
900
|
+
borderColor: themeName === 'dark' ? '#ffffff' : 'rgba(0,0,0,0.2)',
|
|
901
|
+
borderRadius: 8,
|
|
902
|
+
marginTop: -20,
|
|
903
|
+
marginBottom: 10,
|
|
904
|
+
paddingHorizontal: 12,
|
|
905
|
+
paddingVertical: 6,
|
|
906
|
+
},
|
|
907
|
+
modalOption: {
|
|
908
|
+
paddingVertical: 14,
|
|
909
|
+
},
|
|
910
|
+
modalOptionText: {
|
|
911
|
+
...typography.styles.bodyLarge,
|
|
912
|
+
color: colors.text,
|
|
913
|
+
},
|
|
914
|
+
debugButtonsContainer: {
|
|
915
|
+
marginBottom: 16,
|
|
916
|
+
},
|
|
917
|
+
debugButton: {
|
|
918
|
+
paddingHorizontal: 16,
|
|
919
|
+
paddingVertical: 12,
|
|
920
|
+
borderRadius: 8,
|
|
921
|
+
marginBottom: 8,
|
|
922
|
+
alignItems: 'center',
|
|
923
|
+
},
|
|
924
|
+
debugButtonText: {
|
|
925
|
+
...typography.styles.bodyMedium,
|
|
926
|
+
fontWeight: '600',
|
|
927
|
+
},
|
|
928
|
+
errorContainer: {
|
|
929
|
+
flexDirection: 'row',
|
|
930
|
+
alignItems: 'center',
|
|
931
|
+
marginTop: -20,
|
|
932
|
+
marginBottom: 20,
|
|
933
|
+
},
|
|
934
|
+
errorIcon: {
|
|
935
|
+
marginRight: 6,
|
|
936
|
+
},
|
|
937
|
+
errorText: {
|
|
938
|
+
fontSize: 12,
|
|
939
|
+
color: colors.error || '#FF0000',
|
|
940
|
+
fontWeight: '500',
|
|
941
|
+
flex: 1,
|
|
942
|
+
},
|
|
943
|
+
loadingOverlay: {
|
|
944
|
+
position: 'absolute',
|
|
945
|
+
top: 0,
|
|
946
|
+
left: 0,
|
|
947
|
+
right: 0,
|
|
948
|
+
bottom: 0,
|
|
949
|
+
backgroundColor: 'rgba(0, 0, 0, 0.3)',
|
|
950
|
+
justifyContent: 'center',
|
|
951
|
+
alignItems: 'center',
|
|
952
|
+
zIndex: 1000,
|
|
953
|
+
},
|
|
954
|
+
});
|
|
955
|
+
|
|
956
|
+
export default ReviewKYC;
|