@finspringinnovations/fdsdk 0.0.1 → 0.0.3
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/lib/components/CompanyHeader.js +40 -36
- package/lib/components/InterestRateCard.js +3 -2
- package/lib/components/PaymentDetailsCard.js +8 -7
- package/lib/components/PendingFDBottomSheet.js +2 -2
- package/lib/components/TextFieldWithLabel.js +1 -1
- package/lib/components/TrustBox.js +2 -2
- package/lib/config/appDataConfig.d.ts +53 -2
- package/lib/config/appDataConfig.js +39 -6
- package/lib/hooks/usePaymentSSE.d.ts +24 -0
- package/lib/hooks/usePaymentSSE.js +135 -0
- package/lib/hooks/usePaymentStatusTimer.d.ts +25 -0
- package/lib/hooks/usePaymentStatusTimer.js +122 -0
- package/lib/index.d.ts +2 -2
- package/lib/index.js +5 -2
- package/lib/navigation/RootNavigator.js +6 -1
- package/lib/navigation/index.d.ts +2 -0
- package/lib/navigation/index.js +13 -11
- package/lib/screens/FDCalculator.d.ts +1 -0
- package/lib/screens/FDCalculator.js +64 -48
- package/lib/screens/FDList.js +10 -12
- package/lib/screens/NomineeDetail.js +21 -15
- package/lib/screens/PayNow.js +6 -6
- package/lib/screens/Payment.d.ts +1 -0
- package/lib/screens/Payment.js +34 -13
- package/lib/screens/PaymentStatus.js +33 -42
- package/lib/theme/ThemeContext.d.ts +2 -0
- package/lib/theme/ThemeContext.js +4 -2
- package/lib/theme/index.d.ts +6 -1
- package/lib/theme/index.js +24 -8
- package/lib/utils/sseParser.d.ts +7 -0
- package/lib/utils/sseParser.js +27 -0
- package/package.json +2 -2
- package/src/components/CompanyHeader.tsx +50 -44
- package/src/components/InterestRateCard.tsx +3 -2
- package/src/components/PaymentDetailsCard.tsx +45 -40
- package/src/components/PendingFDBottomSheet.tsx +2 -2
- package/src/components/TextFieldWithLabel.tsx +1 -1
- package/src/components/TrustBox.tsx +2 -2
- package/src/config/appDataConfig.ts +70 -5
- package/src/hooks/usePaymentSSE.ts +155 -0
- package/src/hooks/usePaymentStatusTimer.ts +169 -0
- package/src/index.tsx +4 -1
- package/src/navigation/RootNavigator.tsx +7 -0
- package/src/navigation/index.tsx +16 -17
- package/src/screens/FDCalculator.tsx +64 -40
- package/src/screens/FDList.tsx +11 -11
- package/src/screens/NomineeDetail.tsx +20 -14
- package/src/screens/PayNow.tsx +7 -7
- package/src/screens/Payment.tsx +45 -14
- package/src/screens/PaymentStatus.tsx +44 -57
- package/src/theme/ThemeContext.tsx +6 -1
- package/src/theme/index.ts +30 -8
- package/src/utils/sseParser.ts +40 -0
package/src/navigation/index.tsx
CHANGED
|
@@ -9,12 +9,14 @@ import { ApiProvider } from '../providers/ApiProvider';
|
|
|
9
9
|
import { MasterDataProvider } from '../providers/MasterDataProvider';
|
|
10
10
|
import { navigationRef } from './helpers';
|
|
11
11
|
import type { ThemeName, Theme } from '../theme';
|
|
12
|
+
import { getSDKColors, type CustomColors } from '../config/appDataConfig';
|
|
12
13
|
|
|
13
14
|
interface SDKNavigationContainerProps {
|
|
14
15
|
config?: SDKNavigationConfig;
|
|
15
16
|
onExit?: (fdDetails?: any) => void;
|
|
16
17
|
children?: React.ReactNode;
|
|
17
18
|
theme?: ThemeName | Theme;
|
|
19
|
+
colors?: CustomColors; // Custom color overrides (prop-level, highest priority)
|
|
18
20
|
useReactNavigation?: boolean; // Flag to use React Navigation vs Simple Navigator
|
|
19
21
|
}
|
|
20
22
|
|
|
@@ -24,8 +26,15 @@ export const SDKNavigationContainer: React.FC<SDKNavigationContainerProps> = ({
|
|
|
24
26
|
onExit,
|
|
25
27
|
children,
|
|
26
28
|
theme,
|
|
29
|
+
colors: propColors,
|
|
27
30
|
useReactNavigation = true, // Default to React Navigation
|
|
28
31
|
}) => {
|
|
32
|
+
// Merge color overrides: prop-level colors take priority over global SDK colors
|
|
33
|
+
const globalColors = getSDKColors();
|
|
34
|
+
const colorOverrides = (propColors || globalColors)
|
|
35
|
+
? { ...(globalColors || {}), ...(propColors || {}) }
|
|
36
|
+
: null;
|
|
37
|
+
|
|
29
38
|
// Choose navigator based on flag
|
|
30
39
|
const navigator = useReactNavigation
|
|
31
40
|
? <RootNavigator config={config} onExit={onExit} />
|
|
@@ -41,27 +50,17 @@ export const SDKNavigationContainer: React.FC<SDKNavigationContainerProps> = ({
|
|
|
41
50
|
)
|
|
42
51
|
);
|
|
43
52
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
<ApiProvider>
|
|
51
|
-
<MasterDataProvider>
|
|
52
|
-
<ThemeProvider {...themeProps}>
|
|
53
|
-
{content}
|
|
54
|
-
</ThemeProvider>
|
|
55
|
-
</MasterDataProvider>
|
|
56
|
-
</ApiProvider>
|
|
57
|
-
);
|
|
58
|
-
}
|
|
53
|
+
// Build ThemeProvider props
|
|
54
|
+
const themeProps = typeof theme === 'string'
|
|
55
|
+
? { initialTheme: theme, colorOverrides }
|
|
56
|
+
: theme
|
|
57
|
+
? { theme, colorOverrides }
|
|
58
|
+
: { colorOverrides };
|
|
59
59
|
|
|
60
|
-
// Default theme provider with API provider
|
|
61
60
|
return (
|
|
62
61
|
<ApiProvider>
|
|
63
62
|
<MasterDataProvider>
|
|
64
|
-
<ThemeProvider>
|
|
63
|
+
<ThemeProvider {...themeProps}>
|
|
65
64
|
{content}
|
|
66
65
|
</ThemeProvider>
|
|
67
66
|
</MasterDataProvider>
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
Platform,
|
|
12
12
|
BackHandler,
|
|
13
13
|
Keyboard,
|
|
14
|
+
Alert,
|
|
14
15
|
} from 'react-native';
|
|
15
16
|
import Icon from 'react-native-vector-icons/Ionicons';
|
|
16
17
|
import SafeAreaWrapper from '../components/SafeAreaWrapper';
|
|
@@ -40,6 +41,7 @@ import { base64Images } from '../constants/strings/base64Images';
|
|
|
40
41
|
|
|
41
42
|
export interface FDCalculatorProps {
|
|
42
43
|
onGoBack?: () => void;
|
|
44
|
+
onExitSDK?: () => void;
|
|
43
45
|
onNavigateToReviewKYC?: () => void;
|
|
44
46
|
fdData?: {
|
|
45
47
|
id: string;
|
|
@@ -54,7 +56,7 @@ export interface FDCalculatorProps {
|
|
|
54
56
|
};
|
|
55
57
|
}
|
|
56
58
|
|
|
57
|
-
const FDCalculator: React.FC<FDCalculatorProps> = ({ onGoBack, onNavigateToReviewKYC, fdData }) => {
|
|
59
|
+
const FDCalculator: React.FC<FDCalculatorProps> = ({ onGoBack, onExitSDK, onNavigateToReviewKYC, fdData }) => {
|
|
58
60
|
const typography = useTypography();
|
|
59
61
|
const colors = useColors();
|
|
60
62
|
const { themeName } = useTheme();
|
|
@@ -200,16 +202,6 @@ const FDCalculator: React.FC<FDCalculatorProps> = ({ onGoBack, onNavigateToRevie
|
|
|
200
202
|
setPayoutValue(defaultOption);
|
|
201
203
|
}
|
|
202
204
|
}, [payoutOptions]);
|
|
203
|
-
|
|
204
|
-
React.useEffect(() => {
|
|
205
|
-
if (amount && payoutValue) {
|
|
206
|
-
// Only call if amount is valid
|
|
207
|
-
const numericValue = parseInt(amount.replace(/,/g, ''), 10);
|
|
208
|
-
if (numericValue >= 5000 && numericValue <= 50000000 && numericValue % 1000 === 0) {
|
|
209
|
-
handleCalculateFD(amount, payoutValue);
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
}, [payoutValue]);
|
|
213
205
|
// Master data processing complete
|
|
214
206
|
|
|
215
207
|
// FD Calculator API
|
|
@@ -232,6 +224,17 @@ const FDCalculator: React.FC<FDCalculatorProps> = ({ onGoBack, onNavigateToRevie
|
|
|
232
224
|
}, [interestRates]);
|
|
233
225
|
|
|
234
226
|
const [tenureValue, setTenureValue] = useState<string>('');
|
|
227
|
+
|
|
228
|
+
// Effect to recalculate when payout or tenure changes
|
|
229
|
+
React.useEffect(() => {
|
|
230
|
+
if (amount && payoutValue && tenureValue) {
|
|
231
|
+
// Only call if amount is valid
|
|
232
|
+
const numericValue = parseInt(amount.replace(/,/g, ''), 10);
|
|
233
|
+
if (numericValue >= 5000 && numericValue <= 50000000 && numericValue % 1000 === 0) {
|
|
234
|
+
handleCalculateFD(amount, payoutValue, tenureValue);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}, [payoutValue, tenureValue]);
|
|
235
238
|
React.useEffect(() => {
|
|
236
239
|
if (tenureOptions.length > 0) {
|
|
237
240
|
// If effectiveFdData has tenure, use it; otherwise use first option
|
|
@@ -253,15 +256,16 @@ const FDCalculator: React.FC<FDCalculatorProps> = ({ onGoBack, onNavigateToRevie
|
|
|
253
256
|
};
|
|
254
257
|
|
|
255
258
|
// Function to call FD calculator API
|
|
256
|
-
const handleCalculateFD = React.useCallback(async (currentAmount?: string, selectedPayout?: string) => {
|
|
259
|
+
const handleCalculateFD = React.useCallback(async (currentAmount?: string, selectedPayout?: string, selectedTenure?: string) => {
|
|
257
260
|
const payoutToUse = selectedPayout || payoutValue;
|
|
258
261
|
const amountToUse = currentAmount || amount; // ✅ use latest if provided
|
|
262
|
+
const tenureToUse = selectedTenure || tenureValue; // ✅ use latest if provided
|
|
259
263
|
|
|
260
|
-
if (!amountToUse || !payoutToUse) return;
|
|
264
|
+
if (!amountToUse || !payoutToUse || !tenureToUse) return;
|
|
261
265
|
|
|
262
266
|
try {
|
|
263
267
|
const amountValue = parseFloat(amountToUse.replace(/,/g, ''));
|
|
264
|
-
const tenureMonths = parseInt(
|
|
268
|
+
const tenureMonths = parseInt(tenureToUse.replace(' Months', ''));
|
|
265
269
|
|
|
266
270
|
if (isNaN(amountValue) || isNaN(tenureMonths) || amountValue <= 0 || tenureMonths <= 0) return;
|
|
267
271
|
|
|
@@ -272,7 +276,7 @@ const FDCalculator: React.FC<FDCalculatorProps> = ({ onGoBack, onNavigateToRevie
|
|
|
272
276
|
|
|
273
277
|
const appData = getAppData();
|
|
274
278
|
const userDob = appData?.dob || '1990-01-01';
|
|
275
|
-
const isWomenDepositor = appData?.gender === '
|
|
279
|
+
const isWomenDepositor = appData?.gender === 'Female';
|
|
276
280
|
|
|
277
281
|
const requestData = {
|
|
278
282
|
principalAmount: amountValue,
|
|
@@ -286,6 +290,7 @@ const FDCalculator: React.FC<FDCalculatorProps> = ({ onGoBack, onNavigateToRevie
|
|
|
286
290
|
|
|
287
291
|
const result = await calculateFD(requestData).unwrap();
|
|
288
292
|
setCalculationResult(result);
|
|
293
|
+
console.log(';following is the calculation result', result, 'requestData', requestData, 'appData', appData)
|
|
289
294
|
} catch (error) {
|
|
290
295
|
// FD calculation failed
|
|
291
296
|
} finally {
|
|
@@ -326,10 +331,10 @@ const FDCalculator: React.FC<FDCalculatorProps> = ({ onGoBack, onNavigateToRevie
|
|
|
326
331
|
|
|
327
332
|
// Step 7: Debounced API call
|
|
328
333
|
const isValidLocal = numericValue >= 5000 && numericValue <= 50000000 && numericValue % 1000 === 0;
|
|
329
|
-
if (cleanedText && isValidLocal && payoutValue) {
|
|
334
|
+
if (cleanedText && isValidLocal && payoutValue && tenureValue) {
|
|
330
335
|
const newTimer = setTimeout(() => {
|
|
331
|
-
// Pass latest
|
|
332
|
-
handleCalculateFD(cleanedText);
|
|
336
|
+
// Pass latest values directly to avoid stale state
|
|
337
|
+
handleCalculateFD(cleanedText, payoutValue, tenureValue);
|
|
333
338
|
}, 1000);
|
|
334
339
|
setDebounceTimer(newTimer);
|
|
335
340
|
}
|
|
@@ -358,6 +363,30 @@ const FDCalculator: React.FC<FDCalculatorProps> = ({ onGoBack, onNavigateToRevie
|
|
|
358
363
|
return;
|
|
359
364
|
}
|
|
360
365
|
|
|
366
|
+
const appData = getAppData();
|
|
367
|
+
|
|
368
|
+
const hasEssentialData = appData?.panNumber?.trim();
|
|
369
|
+
|
|
370
|
+
if(!hasEssentialData) {
|
|
371
|
+
const alertMessage = appData?.startFDAlertMessage || 'Please complete your KYC details to start an FD, Update your profile in the app and try again.';
|
|
372
|
+
Alert.alert(
|
|
373
|
+
'Action Required',
|
|
374
|
+
alertMessage,
|
|
375
|
+
[
|
|
376
|
+
{
|
|
377
|
+
text: 'OK',
|
|
378
|
+
onPress: () => {
|
|
379
|
+
if (onExitSDK) {
|
|
380
|
+
onExitSDK();
|
|
381
|
+
}
|
|
382
|
+
},
|
|
383
|
+
},
|
|
384
|
+
],
|
|
385
|
+
{ cancelable: false }
|
|
386
|
+
);
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
|
|
361
390
|
// Get providerId from interest rates data
|
|
362
391
|
const sdr = interestRates?.data?.sdrScheme || [];
|
|
363
392
|
const fdr = interestRates?.data?.fdrScheme || [];
|
|
@@ -365,7 +394,6 @@ const FDCalculator: React.FC<FDCalculatorProps> = ({ onGoBack, onNavigateToRevie
|
|
|
365
394
|
const providerId = firstRate?.providerId;
|
|
366
395
|
|
|
367
396
|
// Get user data for all required fields
|
|
368
|
-
const appData = getAppData();
|
|
369
397
|
const userDob = appData?.dob || '1990-01-01';
|
|
370
398
|
const isWomenDepositor = appData?.gender === 'F';
|
|
371
399
|
|
|
@@ -591,19 +619,18 @@ const FDCalculator: React.FC<FDCalculatorProps> = ({ onGoBack, onNavigateToRevie
|
|
|
591
619
|
<SafeAreaWrapper
|
|
592
620
|
includeTop={false}
|
|
593
621
|
bottomPadding={25}
|
|
594
|
-
statusBarColor=
|
|
595
|
-
statusBarStyle=
|
|
622
|
+
statusBarColor={colors.background}
|
|
623
|
+
statusBarStyle={themeName === 'dark' ? 'light-content' : 'dark-content'}
|
|
596
624
|
>
|
|
597
625
|
{/* Header */}
|
|
598
626
|
<View style={styles.header}>
|
|
599
627
|
<TouchableOpacity onPress={() => navigate('FDList')} style={styles.backButton}>
|
|
600
628
|
<Image
|
|
601
629
|
source={{ uri: base64Images.backArrow }}
|
|
602
|
-
style={[styles.backIcon, { tintColor:
|
|
630
|
+
style={[styles.backIcon, { tintColor: colors.text }]}
|
|
603
631
|
resizeMode="contain"
|
|
604
632
|
width={24}
|
|
605
633
|
height={24}
|
|
606
|
-
|
|
607
634
|
/>
|
|
608
635
|
</TouchableOpacity>
|
|
609
636
|
</View>
|
|
@@ -656,8 +683,8 @@ const FDCalculator: React.FC<FDCalculatorProps> = ({ onGoBack, onNavigateToRevie
|
|
|
656
683
|
onPress={() => {
|
|
657
684
|
setTenureValue(option);
|
|
658
685
|
setShowTenureMenu(false);
|
|
659
|
-
// Call API
|
|
660
|
-
setTimeout(() => handleCalculateFD(), 100);
|
|
686
|
+
// Call API with the new tenure value directly to avoid closure issues
|
|
687
|
+
setTimeout(() => handleCalculateFD(amount, payoutValue, option), 100);
|
|
661
688
|
}}
|
|
662
689
|
>
|
|
663
690
|
<Text style={styles.modalOptionText}>{option}</Text>
|
|
@@ -688,9 +715,8 @@ const FDCalculator: React.FC<FDCalculatorProps> = ({ onGoBack, onNavigateToRevie
|
|
|
688
715
|
onPress={() => {
|
|
689
716
|
setPayoutValue(option);
|
|
690
717
|
setShowPayoutModal(false);
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
// setTimeout(() => handleCalculateFD(option), 100);
|
|
718
|
+
// Pass all values directly to avoid stale state
|
|
719
|
+
handleCalculateFD(amount, option, tenureValue);
|
|
694
720
|
}}
|
|
695
721
|
>
|
|
696
722
|
<Text style={styles.modalOptionText}>{option}</Text>
|
|
@@ -893,34 +919,32 @@ const createStyles = (typography: any, colors: any, themeName: string) => StyleS
|
|
|
893
919
|
position: 'relative',
|
|
894
920
|
},
|
|
895
921
|
startButton: {
|
|
896
|
-
backgroundColor:
|
|
897
|
-
paddingVertical:
|
|
922
|
+
backgroundColor: colors.buttonBackground || colors.headerBg,
|
|
923
|
+
paddingVertical: 20,
|
|
898
924
|
paddingHorizontal: 20,
|
|
899
|
-
borderRadius:
|
|
925
|
+
borderRadius: 30,
|
|
900
926
|
marginTop: 30,
|
|
901
927
|
alignItems: 'center',
|
|
902
928
|
justifyContent: 'center',
|
|
903
|
-
height:
|
|
929
|
+
height: 56,
|
|
904
930
|
width: '90%',
|
|
905
931
|
alignSelf: 'center',
|
|
906
932
|
},
|
|
907
933
|
startButtonDisabled: {
|
|
908
|
-
backgroundColor: '#909090',
|
|
909
|
-
color: '#ffffff',
|
|
934
|
+
backgroundColor: colors.muted || '#909090',
|
|
910
935
|
},
|
|
911
936
|
startButtonText: {
|
|
912
937
|
...typography.styles.button,
|
|
913
|
-
color:
|
|
914
|
-
lineHeight: themeName === 'dark' ? 24 : typography.styles.button.lineHeight,
|
|
938
|
+
color: colors.buttonTextColor || colors.background,
|
|
915
939
|
fontSize: 16,
|
|
916
940
|
},
|
|
917
941
|
startButtonTextDisabled: {
|
|
918
|
-
color:
|
|
942
|
+
color: colors.textSecondary,
|
|
919
943
|
},
|
|
920
944
|
inlineMenu: {
|
|
921
|
-
backgroundColor:
|
|
922
|
-
borderWidth:
|
|
923
|
-
borderColor:
|
|
945
|
+
backgroundColor: colors.inputBackground || colors.background,
|
|
946
|
+
borderWidth: 0.5,
|
|
947
|
+
borderColor: colors.inputBorder || colors.border,
|
|
924
948
|
borderRadius: 8,
|
|
925
949
|
marginTop: 6,
|
|
926
950
|
paddingHorizontal: 12,
|
package/src/screens/FDList.tsx
CHANGED
|
@@ -888,7 +888,7 @@ const FDList: React.FC<FDListProps> = ({
|
|
|
888
888
|
<SafeAreaWrapper
|
|
889
889
|
style={customStyles.container}
|
|
890
890
|
includeTop={false}
|
|
891
|
-
statusBarColor=
|
|
891
|
+
statusBarColor={colors.headerBg}
|
|
892
892
|
statusBarStyle="light-content"
|
|
893
893
|
>
|
|
894
894
|
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
|
|
@@ -903,7 +903,7 @@ const FDList: React.FC<FDListProps> = ({
|
|
|
903
903
|
<SafeAreaWrapper
|
|
904
904
|
style={customStyles.container}
|
|
905
905
|
includeTop={false}
|
|
906
|
-
statusBarColor=
|
|
906
|
+
statusBarColor={colors.headerBg}
|
|
907
907
|
statusBarStyle="light-content"
|
|
908
908
|
>
|
|
909
909
|
{/* Custom FDList Header */}
|
|
@@ -923,12 +923,12 @@ const FDList: React.FC<FDListProps> = ({
|
|
|
923
923
|
<View style={styles.headerContent}>
|
|
924
924
|
<Text style={styles.headerTitle}>{FD_STRINGS.CORPORATE_FDS_TITLE}</Text>
|
|
925
925
|
<View style={styles.poweredByContainer}>
|
|
926
|
-
<Text style={styles.poweredByText}>Powered by</Text>
|
|
926
|
+
{/* <Text style={styles.poweredByText}>Powered by</Text>
|
|
927
927
|
<Image
|
|
928
928
|
source={{ uri: base64Images.simplfylogo }}
|
|
929
929
|
style={styles.simplifyLogo}
|
|
930
930
|
resizeMode="contain"
|
|
931
|
-
/>
|
|
931
|
+
/> */}
|
|
932
932
|
</View>
|
|
933
933
|
</View>
|
|
934
934
|
</View>
|
|
@@ -1105,7 +1105,7 @@ const createStyles = (colors: ColorScheme, typography: any, spacing: any, themeN
|
|
|
1105
1105
|
backgroundColor: colors.surface,
|
|
1106
1106
|
},
|
|
1107
1107
|
customHeader: {
|
|
1108
|
-
backgroundColor:
|
|
1108
|
+
backgroundColor: colors.headerBg,
|
|
1109
1109
|
paddingTop: Platform.OS === 'ios' ? 50 : 20,
|
|
1110
1110
|
paddingBottom: spacing.md,
|
|
1111
1111
|
paddingHorizontal: spacing.lg,
|
|
@@ -1121,7 +1121,7 @@ const createStyles = (colors: ColorScheme, typography: any, spacing: any, themeN
|
|
|
1121
1121
|
backIcon: {
|
|
1122
1122
|
width: 24,
|
|
1123
1123
|
height: 24,
|
|
1124
|
-
tintColor:
|
|
1124
|
+
tintColor: colors.headerText,
|
|
1125
1125
|
},
|
|
1126
1126
|
headerContent: {
|
|
1127
1127
|
flex: 1,
|
|
@@ -1130,7 +1130,7 @@ const createStyles = (colors: ColorScheme, typography: any, spacing: any, themeN
|
|
|
1130
1130
|
},
|
|
1131
1131
|
headerTitle: {
|
|
1132
1132
|
...typography.styles.h3,
|
|
1133
|
-
color:
|
|
1133
|
+
color: colors.headerText,
|
|
1134
1134
|
marginBottom: spacing.xs,
|
|
1135
1135
|
},
|
|
1136
1136
|
poweredByContainer: {
|
|
@@ -1139,7 +1139,7 @@ const createStyles = (colors: ColorScheme, typography: any, spacing: any, themeN
|
|
|
1139
1139
|
},
|
|
1140
1140
|
poweredByText: {
|
|
1141
1141
|
...typography.styles.text10Regular,
|
|
1142
|
-
color:
|
|
1142
|
+
color: colors.headerText,
|
|
1143
1143
|
opacity: 0.7,
|
|
1144
1144
|
marginRight: 0,
|
|
1145
1145
|
},
|
|
@@ -1175,14 +1175,14 @@ const createStyles = (colors: ColorScheme, typography: any, spacing: any, themeN
|
|
|
1175
1175
|
paddingVertical: spacing.md,
|
|
1176
1176
|
marginHorizontal: 2,
|
|
1177
1177
|
borderBottomWidth: 3,
|
|
1178
|
-
borderBottomColor:
|
|
1178
|
+
borderBottomColor: colors.surface,
|
|
1179
1179
|
borderBottomLeftRadius: 8,
|
|
1180
1180
|
borderBottomRightRadius: 8,
|
|
1181
1181
|
backgroundColor: 'transparent',
|
|
1182
1182
|
},
|
|
1183
1183
|
activeTab: {
|
|
1184
1184
|
borderBottomColor: colors.tabSelected,
|
|
1185
|
-
backgroundColor:
|
|
1185
|
+
backgroundColor: colors.background,
|
|
1186
1186
|
borderBottomLeftRadius: 12,
|
|
1187
1187
|
borderBottomRightRadius: 12,
|
|
1188
1188
|
},
|
|
@@ -1276,7 +1276,7 @@ const createStyles = (colors: ColorScheme, typography: any, spacing: any, themeN
|
|
|
1276
1276
|
},
|
|
1277
1277
|
showAllButtonText: {
|
|
1278
1278
|
...typography.styles.text14Medium,
|
|
1279
|
-
color:
|
|
1279
|
+
color: colors.headerText, // Use theme header text color
|
|
1280
1280
|
textAlign: 'center',
|
|
1281
1281
|
},
|
|
1282
1282
|
});
|
|
@@ -140,7 +140,8 @@ const NomineeDetail: React.FC<NomineeDetailProps> = ({ onGoBack, onSave, initial
|
|
|
140
140
|
const year = parseInt(dateParts[2], 10);
|
|
141
141
|
|
|
142
142
|
if (day >= 1 && day <= 31 && month >= 1 && month <= 12 && year >= 1900 && year <= new Date().getFullYear()) {
|
|
143
|
-
|
|
143
|
+
// Use noon to avoid timezone issues
|
|
144
|
+
const parsedDate = new Date(year, month - 1, day, 12, 0, 0);
|
|
144
145
|
if (!isNaN(parsedDate.getTime()) && parsedDate.getDate() === day && parsedDate.getMonth() === month - 1 && parsedDate.getFullYear() === year) {
|
|
145
146
|
setSelectedDate(parsedDate);
|
|
146
147
|
}
|
|
@@ -286,7 +287,8 @@ const NomineeDetail: React.FC<NomineeDetailProps> = ({ onGoBack, onSave, initial
|
|
|
286
287
|
|
|
287
288
|
// Validate the parsed values
|
|
288
289
|
if (day >= 1 && day <= 31 && month >= 1 && month <= 12 && year >= 1900 && year <= new Date().getFullYear()) {
|
|
289
|
-
|
|
290
|
+
// Use noon to avoid timezone issues
|
|
291
|
+
const parsedDate = new Date(year, month - 1, day, 12, 0, 0);
|
|
290
292
|
if (!isNaN(parsedDate.getTime()) && parsedDate.getDate() === day && parsedDate.getMonth() === month - 1 && parsedDate.getFullYear() === year) {
|
|
291
293
|
setSelectedDate(parsedDate);
|
|
292
294
|
} else {
|
|
@@ -320,12 +322,14 @@ const NomineeDetail: React.FC<NomineeDetailProps> = ({ onGoBack, onSave, initial
|
|
|
320
322
|
if (Platform.OS === 'android') {
|
|
321
323
|
// Android fires with types: 'set' and 'dismissed'
|
|
322
324
|
if (event?.type === 'set' && selectedDate) {
|
|
323
|
-
|
|
325
|
+
// Normalize to noon to avoid timezone issues
|
|
326
|
+
const normalizedDate = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), selectedDate.getDate(), 12, 0, 0);
|
|
327
|
+
setSelectedDate(normalizedDate);
|
|
324
328
|
|
|
325
329
|
const today = new Date();
|
|
326
|
-
let age = today.getFullYear() -
|
|
327
|
-
const m = today.getMonth() -
|
|
328
|
-
if (m < 0 || (m === 0 && today.getDate() <
|
|
330
|
+
let age = today.getFullYear() - normalizedDate.getFullYear();
|
|
331
|
+
const m = today.getMonth() - normalizedDate.getMonth();
|
|
332
|
+
if (m < 0 || (m === 0 && today.getDate() < normalizedDate.getDate())) {
|
|
329
333
|
age--;
|
|
330
334
|
}
|
|
331
335
|
|
|
@@ -336,9 +340,9 @@ const NomineeDetail: React.FC<NomineeDetailProps> = ({ onGoBack, onSave, initial
|
|
|
336
340
|
return;
|
|
337
341
|
}
|
|
338
342
|
|
|
339
|
-
const day =
|
|
340
|
-
const month = (
|
|
341
|
-
const year =
|
|
343
|
+
const day = normalizedDate.getDate().toString().padStart(2, '0');
|
|
344
|
+
const month = (normalizedDate.getMonth() + 1).toString().padStart(2, '0');
|
|
345
|
+
const year = normalizedDate.getFullYear().toString();
|
|
342
346
|
const formattedDate = `${day}/${month}/${year}`;
|
|
343
347
|
updateField('dateOfBirth', formattedDate);
|
|
344
348
|
closeDatePicker();
|
|
@@ -353,10 +357,12 @@ const NomineeDetail: React.FC<NomineeDetailProps> = ({ onGoBack, onSave, initial
|
|
|
353
357
|
|
|
354
358
|
// iOS inline/spinner handling
|
|
355
359
|
if (selectedDate) {
|
|
356
|
-
|
|
357
|
-
const
|
|
358
|
-
|
|
359
|
-
const
|
|
360
|
+
// Normalize to noon to avoid timezone issues
|
|
361
|
+
const normalizedDate = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), selectedDate.getDate(), 12, 0, 0);
|
|
362
|
+
setSelectedDate(normalizedDate);
|
|
363
|
+
const day = normalizedDate.getDate().toString().padStart(2, '0');
|
|
364
|
+
const month = (normalizedDate.getMonth() + 1).toString().padStart(2, '0');
|
|
365
|
+
const year = normalizedDate.getFullYear().toString();
|
|
360
366
|
const formattedDate = `${day}/${month}/${year}`;
|
|
361
367
|
updateField('dateOfBirth', formattedDate);
|
|
362
368
|
}
|
|
@@ -499,7 +505,6 @@ const NomineeDetail: React.FC<NomineeDetailProps> = ({ onGoBack, onSave, initial
|
|
|
499
505
|
}}
|
|
500
506
|
editable={false}
|
|
501
507
|
keepWhiteBackground={true}
|
|
502
|
-
inputStyle={{ color: 'white' }}
|
|
503
508
|
/>
|
|
504
509
|
);
|
|
505
510
|
|
|
@@ -679,6 +684,7 @@ const NomineeDetail: React.FC<NomineeDetailProps> = ({ onGoBack, onSave, initial
|
|
|
679
684
|
mode="date"
|
|
680
685
|
display={Platform.OS === 'ios' ? 'spinner' : 'default'}
|
|
681
686
|
onChange={onDateChange}
|
|
687
|
+
minimumDate={new Date(1900, 0, 1)} // January 1, 1900
|
|
682
688
|
maximumDate={new Date(new Date().getFullYear() - 18, new Date().getMonth(), new Date().getDate())}
|
|
683
689
|
themeVariant="light"
|
|
684
690
|
/>
|
package/src/screens/PayNow.tsx
CHANGED
|
@@ -166,8 +166,8 @@ const PayNow: React.FC<PayNowProps> = ({ onGoBack, onConfirm, fdData }) => {
|
|
|
166
166
|
<SafeAreaWrapper
|
|
167
167
|
includeTop={false}
|
|
168
168
|
bottomPadding={25}
|
|
169
|
-
statusBarColor=
|
|
170
|
-
statusBarStyle=
|
|
169
|
+
statusBarColor={colors.background}
|
|
170
|
+
statusBarStyle={themeName === 'dark' ? 'light-content' : 'dark-content'}
|
|
171
171
|
>
|
|
172
172
|
<View style={styles.container}>
|
|
173
173
|
<ScrollView showsVerticalScrollIndicator={false} contentContainerStyle={styles.scrollContent}>
|
|
@@ -243,13 +243,13 @@ const createStyles = (colors: any, typography: any, themeName: string) => StyleS
|
|
|
243
243
|
},
|
|
244
244
|
backButton: {
|
|
245
245
|
flex: 1,
|
|
246
|
-
backgroundColor:
|
|
247
|
-
borderWidth:
|
|
248
|
-
borderColor:
|
|
249
|
-
borderRadius:
|
|
246
|
+
backgroundColor: colors.cancelButtonBg || 'transparent',
|
|
247
|
+
borderWidth: 0,
|
|
248
|
+
borderColor: 'transparent',
|
|
249
|
+
borderRadius: 10,
|
|
250
250
|
},
|
|
251
251
|
backButtonText: {
|
|
252
|
-
color:
|
|
252
|
+
color: colors.primary,
|
|
253
253
|
},
|
|
254
254
|
confirmButton: {
|
|
255
255
|
flex: 1,
|
package/src/screens/Payment.tsx
CHANGED
|
@@ -5,11 +5,15 @@ import SafeAreaWrapper from '../components/SafeAreaWrapper';
|
|
|
5
5
|
import { useColors } from '../theme/ThemeContext';
|
|
6
6
|
import { decryptResponse } from '../utils/encryption';
|
|
7
7
|
import { getEncryptionConfig } from '../config/encryptionConfig';
|
|
8
|
+
import { useFocusEffect } from '@react-navigation/native';
|
|
9
|
+
import { getPaymentSession } from '../state/paymentSession';
|
|
10
|
+
import { usePaymentStatusTimer, PaymentStatus } from '../hooks/usePaymentStatusTimer';
|
|
8
11
|
|
|
9
12
|
export interface PaymentProps {
|
|
10
13
|
onGoBack?: () => void;
|
|
11
14
|
onPaymentSuccess?: (data?: any) => void;
|
|
12
15
|
onPaymentFailure?: (error?: any) => void;
|
|
16
|
+
onPaymentPending?: (data?: any) => void;
|
|
13
17
|
paymentUrl: string;
|
|
14
18
|
successUrl?: string;
|
|
15
19
|
failureUrl?: string;
|
|
@@ -19,6 +23,7 @@ const Payment: React.FC<PaymentProps> = ({
|
|
|
19
23
|
onGoBack,
|
|
20
24
|
onPaymentSuccess,
|
|
21
25
|
onPaymentFailure,
|
|
26
|
+
onPaymentPending,
|
|
22
27
|
paymentUrl,
|
|
23
28
|
successUrl = 'payment/success',
|
|
24
29
|
failureUrl = 'payment/failure',
|
|
@@ -27,6 +32,36 @@ const Payment: React.FC<PaymentProps> = ({
|
|
|
27
32
|
const styles = createStyles(colors);
|
|
28
33
|
const webViewRef = useRef<WebView>(null);
|
|
29
34
|
const [loading, setLoading] = useState(true);
|
|
35
|
+
const currentStatusRef = useRef<PaymentStatus>('pending');
|
|
36
|
+
const { transactionId } = getPaymentSession();
|
|
37
|
+
|
|
38
|
+
const { startTimer, stopTimer } = usePaymentStatusTimer({
|
|
39
|
+
transactionId,
|
|
40
|
+
onStatusUpdate: (status, response) => {
|
|
41
|
+
currentStatusRef.current = status;
|
|
42
|
+
|
|
43
|
+
if (status === 'success') {
|
|
44
|
+
onPaymentSuccess?.(response);
|
|
45
|
+
}
|
|
46
|
+
else if (status === 'failed') {
|
|
47
|
+
onPaymentFailure?.(response);
|
|
48
|
+
}
|
|
49
|
+
else if (status === 'pending') {
|
|
50
|
+
onPaymentPending?.(response);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
useFocusEffect(
|
|
57
|
+
React.useCallback(() => {
|
|
58
|
+
startTimer();
|
|
59
|
+
|
|
60
|
+
}, [startTimer, stopTimer])
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
|
|
30
65
|
|
|
31
66
|
const handleNavigationStateChange = async (navState: any) => {
|
|
32
67
|
const { url } = navState;
|
|
@@ -35,18 +70,9 @@ const Payment: React.FC<PaymentProps> = ({
|
|
|
35
70
|
await handlePaymentStatusPage();
|
|
36
71
|
return;
|
|
37
72
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
onPaymentSuccess?.(navState);
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
if (url.includes(failureUrl)) {
|
|
46
|
-
setLoading(false);
|
|
47
|
-
onPaymentFailure?.(navState);
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
73
|
+
// else{
|
|
74
|
+
// onPaymentPending?.(url);
|
|
75
|
+
// }
|
|
50
76
|
};
|
|
51
77
|
|
|
52
78
|
const handleWebViewError = (syntheticEvent: any) => {
|
|
@@ -107,14 +133,19 @@ const Payment: React.FC<PaymentProps> = ({
|
|
|
107
133
|
const paymentStatus = response.data?.paymentStatus?.toLowerCase();
|
|
108
134
|
|
|
109
135
|
if (paymentStatus === 'success') {
|
|
136
|
+
currentStatusRef.current = "success";
|
|
137
|
+
stopTimer();
|
|
110
138
|
onPaymentSuccess?.(response);
|
|
111
139
|
} else if (paymentStatus === 'failed') {
|
|
140
|
+
currentStatusRef.current = "failed";
|
|
141
|
+
stopTimer();
|
|
112
142
|
onPaymentFailure?.(response);
|
|
113
143
|
} else {
|
|
114
|
-
|
|
144
|
+
onPaymentPending?.(response);
|
|
115
145
|
}
|
|
116
146
|
} else {
|
|
117
|
-
|
|
147
|
+
currentStatusRef.current = "pending";
|
|
148
|
+
onPaymentPending?.(response);
|
|
118
149
|
}
|
|
119
150
|
|
|
120
151
|
setLoading(false);
|