@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.
Files changed (53) hide show
  1. package/lib/components/CompanyHeader.js +40 -36
  2. package/lib/components/InterestRateCard.js +3 -2
  3. package/lib/components/PaymentDetailsCard.js +8 -7
  4. package/lib/components/PendingFDBottomSheet.js +2 -2
  5. package/lib/components/TextFieldWithLabel.js +1 -1
  6. package/lib/components/TrustBox.js +2 -2
  7. package/lib/config/appDataConfig.d.ts +53 -2
  8. package/lib/config/appDataConfig.js +39 -6
  9. package/lib/hooks/usePaymentSSE.d.ts +24 -0
  10. package/lib/hooks/usePaymentSSE.js +135 -0
  11. package/lib/hooks/usePaymentStatusTimer.d.ts +25 -0
  12. package/lib/hooks/usePaymentStatusTimer.js +122 -0
  13. package/lib/index.d.ts +2 -2
  14. package/lib/index.js +5 -2
  15. package/lib/navigation/RootNavigator.js +6 -1
  16. package/lib/navigation/index.d.ts +2 -0
  17. package/lib/navigation/index.js +13 -11
  18. package/lib/screens/FDCalculator.d.ts +1 -0
  19. package/lib/screens/FDCalculator.js +64 -48
  20. package/lib/screens/FDList.js +10 -12
  21. package/lib/screens/NomineeDetail.js +21 -15
  22. package/lib/screens/PayNow.js +6 -6
  23. package/lib/screens/Payment.d.ts +1 -0
  24. package/lib/screens/Payment.js +34 -13
  25. package/lib/screens/PaymentStatus.js +33 -42
  26. package/lib/theme/ThemeContext.d.ts +2 -0
  27. package/lib/theme/ThemeContext.js +4 -2
  28. package/lib/theme/index.d.ts +6 -1
  29. package/lib/theme/index.js +24 -8
  30. package/lib/utils/sseParser.d.ts +7 -0
  31. package/lib/utils/sseParser.js +27 -0
  32. package/package.json +2 -2
  33. package/src/components/CompanyHeader.tsx +50 -44
  34. package/src/components/InterestRateCard.tsx +3 -2
  35. package/src/components/PaymentDetailsCard.tsx +45 -40
  36. package/src/components/PendingFDBottomSheet.tsx +2 -2
  37. package/src/components/TextFieldWithLabel.tsx +1 -1
  38. package/src/components/TrustBox.tsx +2 -2
  39. package/src/config/appDataConfig.ts +70 -5
  40. package/src/hooks/usePaymentSSE.ts +155 -0
  41. package/src/hooks/usePaymentStatusTimer.ts +169 -0
  42. package/src/index.tsx +4 -1
  43. package/src/navigation/RootNavigator.tsx +7 -0
  44. package/src/navigation/index.tsx +16 -17
  45. package/src/screens/FDCalculator.tsx +64 -40
  46. package/src/screens/FDList.tsx +11 -11
  47. package/src/screens/NomineeDetail.tsx +20 -14
  48. package/src/screens/PayNow.tsx +7 -7
  49. package/src/screens/Payment.tsx +45 -14
  50. package/src/screens/PaymentStatus.tsx +44 -57
  51. package/src/theme/ThemeContext.tsx +6 -1
  52. package/src/theme/index.ts +30 -8
  53. package/src/utils/sseParser.ts +40 -0
@@ -6,7 +6,7 @@ import SafeAreaWrapper from '../components/SafeAreaWrapper';
6
6
  import { PaymentDetailsCard } from '../components';
7
7
  import ActionButton from '../components/ActionButton';
8
8
  import { useColors, useTypography, useTheme } from '../theme/ThemeContext';
9
- import { usePaymentReverseFeedMutation, usePaymentRetryMutation } from '../api/fdApi';
9
+ import { usePaymentRetryMutation } from '../api/fdApi';
10
10
  import { useGetCustomerApplicationsMutation } from '../api/customerApi';
11
11
  import { useAppSelector } from '../store';
12
12
  import { getUserInfoForAPI } from '../config/appDataConfig';
@@ -16,6 +16,7 @@ import { useRoute } from '@react-navigation/native';
16
16
  import { BANK_STRINGS } from '../constants/strings/bank';
17
17
  import { COMMON_STRINGS } from '../constants/strings/common';
18
18
  import { getApiConfig } from '../config/apiConfig';
19
+ import { usePaymentStatusTimer } from '../hooks/usePaymentStatusTimer';
19
20
 
20
21
  export interface PaymentStatusProps {
21
22
  onRetry?: () => void;
@@ -45,7 +46,7 @@ const PaymentStatus: React.FC<PaymentStatusProps> = ({
45
46
  // Local state to track current status (can be updated from API responses)
46
47
  const [currentStatus, setCurrentStatus] = useState<'success' | 'failed' | 'pending'>(status);
47
48
 
48
- const styles = createStyles(colors, typography, currentStatus);
49
+ const styles = createStyles(colors, typography, currentStatus, themeName);
49
50
  const route = useRoute();
50
51
 
51
52
  // Get transaction ID from navigation props (route params)
@@ -63,12 +64,23 @@ const PaymentStatus: React.FC<PaymentStatusProps> = ({
63
64
  const entityId = useAppSelector((state: any) => state?.onboarding?.entityid);
64
65
  const providerId = useAppSelector((state: any) => state?.onboarding?.providerId);
65
66
 
66
- // Payment Reverse Feed API
67
- const [paymentReverseFeed, {
68
- data: paymentReverseFeedResponse,
69
- error: paymentReverseFeedError,
70
- isLoading: isLoadingPaymentReverseFeed,
71
- }] = usePaymentReverseFeedMutation();
67
+ const {
68
+ startTimer,
69
+ stopTimer,
70
+ triggerStatusCheck,
71
+ isCheckingStatus,
72
+ } = usePaymentStatusTimer({
73
+ transactionId: finalTransactionId,
74
+ overrides: {
75
+ providerId,
76
+ workflowInstanceId,
77
+ applicationid: applicationId,
78
+ entityid: entityId,
79
+ },
80
+ onStatusUpdate: (nextStatus) => {
81
+ setCurrentStatus(nextStatus);
82
+ },
83
+ });
72
84
 
73
85
  // Payment Retry API
74
86
  const [paymentRetry, {
@@ -105,43 +117,20 @@ const PaymentStatus: React.FC<PaymentStatusProps> = ({
105
117
  return amount.toLocaleString('en-IN');
106
118
  };
107
119
 
108
- // Handle payment reverse feed API call for pending status
109
- const handlePaymentReverseFeed = async () => {
110
-
111
- try {
112
-
113
- // Get user info from app data
114
- const userInfo = getUserInfoForAPI();
115
-
116
- // Prepare the API request
117
- const paymentReverseFeedRequest = {
118
- // Headers
119
- providerId: providerId,
120
- workflowInstanceId: workflowInstanceId,
121
- userreferenceid: userInfo.id,
122
- applicationid: applicationId,
123
- entityid: entityId,
124
- // Body
125
- transactionId: finalTransactionId,
126
- };
127
-
128
- const response = await paymentReverseFeed(paymentReverseFeedRequest).unwrap();
129
-
130
- // Handle the response based on payment status
131
- if (response?.data?.paymentStatus?.toLowerCase() === 'success') {
132
- setCurrentStatus('success');
133
-
134
- } else if (response?.data?.paymentStatus?.toLowerCase() === 'failed') {
135
- setCurrentStatus('failed');
136
-
137
- }
120
+ useEffect(() => {
121
+ if (currentStatus === 'pending') {
122
+ startTimer();
123
+ } else {
124
+ stopTimer();
125
+ }
138
126
 
139
- } catch (error) {
127
+ return () => {
128
+ stopTimer();
129
+ };
130
+ }, [currentStatus, startTimer, stopTimer]);
140
131
 
141
- // Alert.alert(COMMON_STRINGS.ERROR, BANK_STRINGS.PAYMENT_GATEWAY_ERROR, [
142
- // { text: COMMON_STRINGS.OK }
143
- // ]);
144
- }
132
+ const handlePaymentReverseFeed = async () => {
133
+ await triggerStatusCheck();
145
134
  };
146
135
 
147
136
  // Handle payment retry when status is failed
@@ -321,10 +310,10 @@ const PaymentStatus: React.FC<PaymentStatusProps> = ({
321
310
  <SafeAreaWrapper
322
311
  includeTop={true}
323
312
  bottomPadding={0}
324
- statusBarColor="#000000"
325
- statusBarStyle="light-content"
313
+ statusBarColor={colors.background}
314
+ statusBarStyle={themeName === 'dark' ? 'light-content' : 'dark-content'}
326
315
  >
327
- {Platform.OS === 'ios' && <StatusBar barStyle="light-content" />}
316
+ {Platform.OS === 'ios' && <StatusBar barStyle={themeName === 'dark' ? 'light-content' : 'dark-content'} />}
328
317
 
329
318
  <View style={styles.container}>
330
319
  {/* <Text style={styles.headerTitle}>
@@ -423,22 +412,21 @@ const PaymentStatus: React.FC<PaymentStatusProps> = ({
423
412
  disabled={isLoadingPaymentRetry}
424
413
  />
425
414
  )}
426
- {currentStatus === 'pending' && (
415
+ {/* {currentStatus === 'pending' && (
427
416
  <ActionButton
428
- title={isLoadingPaymentReverseFeed ? COMMON_STRINGS.CHECKING : BANK_STRINGS.REFRESH_STATUS_BUTTON}
417
+ title={isCheckingStatus ? COMMON_STRINGS.CHECKING : BANK_STRINGS.REFRESH_STATUS_BUTTON}
429
418
  onPress={handlePaymentReverseFeed}
430
- disabled={isLoadingPaymentReverseFeed}
419
+ disabled={isCheckingStatus}
431
420
  />
432
- )}
421
+ )} */}
433
422
  </View>
434
423
  </View>
435
424
  </SafeAreaWrapper>
436
425
  );
437
426
  };
438
427
 
439
- const createStyles = (colors: any, typography: any, status: 'success' | 'failed' | 'pending') => {
440
- const statusColor = status === 'success' ? '#4CAF50' : status === 'failed' ? '#F44336' : '#FF9800';
441
- const cardBgColor = status === 'success' ? '#E8F5F0' : status === 'failed' ? '#FFF3F3' : '#FFF8E1';
428
+ const createStyles = (colors: any, typography: any, status: 'success' | 'failed' | 'pending', themeName: string) => {
429
+ const isDark = themeName === 'dark';
442
430
 
443
431
  return StyleSheet.create({
444
432
  container: {
@@ -475,7 +463,7 @@ const createStyles = (colors: any, typography: any, status: 'success' | 'failed'
475
463
  width: 60,
476
464
  height: 60,
477
465
  borderRadius: 30,
478
- backgroundColor: status === 'success' ? '#ffffff' : status === 'failed' ? 'rgba(244, 67, 54, 0.0)' : 'rgba(255, 152, 0, 0.2)',
466
+ backgroundColor: status === 'success' ? (isDark ? colors.surface : '#ffffff') : status === 'failed' ? 'rgba(244, 67, 54, 0.0)' : 'rgba(255, 152, 0, 0.2)',
479
467
  alignItems: 'center',
480
468
  justifyContent: 'center',
481
469
  },
@@ -492,7 +480,6 @@ const createStyles = (colors: any, typography: any, status: 'success' | 'failed'
492
480
  textAlign: 'center',
493
481
  marginBottom: 30,
494
482
  marginTop: -30,
495
-
496
483
  },
497
484
  transactionLabel: {
498
485
  ...typography.styles.bodyMedium,
@@ -516,7 +503,7 @@ const createStyles = (colors: any, typography: any, status: 'success' | 'failed'
516
503
  width: 56,
517
504
  height: 56,
518
505
  borderRadius: 28,
519
- backgroundColor: '#5F9E8F',
506
+ backgroundColor: colors.primary,
520
507
  alignItems: 'center',
521
508
  justifyContent: 'center',
522
509
  borderWidth: 4,
@@ -525,7 +512,7 @@ const createStyles = (colors: any, typography: any, status: 'success' | 'failed'
525
512
  avatarText: {
526
513
  fontSize: 24,
527
514
  fontWeight: '600',
528
- color: 'white',
515
+ color: colors.headerText,
529
516
  },
530
517
  footer: {
531
518
  position: 'absolute',
@@ -1,5 +1,6 @@
1
1
  import React, { createContext, useContext, ReactNode } from 'react';
2
2
  import { createTheme, type Theme, type ThemeName, defaultTheme } from './index';
3
+ import type { CustomColors } from '../config/appDataConfig';
3
4
 
4
5
  interface ThemeContextType {
5
6
  theme: Theme;
@@ -13,16 +14,20 @@ interface ThemeProviderProps {
13
14
  children: ReactNode;
14
15
  initialTheme?: ThemeName;
15
16
  theme?: Theme; // Allow custom theme override
17
+ colorOverrides?: CustomColors | null; // Custom color overrides from SDK initialization
16
18
  }
17
19
 
18
20
  export const ThemeProvider: React.FC<ThemeProviderProps> = ({
19
21
  children,
20
22
  initialTheme = 'primary',
21
23
  theme: customTheme,
24
+ colorOverrides,
22
25
  }) => {
23
26
  const [currentThemeName, setCurrentThemeName] = React.useState<ThemeName>(initialTheme);
24
27
 
25
- const theme = customTheme || createTheme(currentThemeName);
28
+ // If a full custom theme is provided, use it directly.
29
+ // Otherwise, create theme from name and merge any color overrides on top.
30
+ const theme = customTheme || createTheme(currentThemeName, colorOverrides);
26
31
 
27
32
  const setTheme = React.useCallback((themeName: ThemeName) => {
28
33
  setCurrentThemeName(themeName);
@@ -1,6 +1,7 @@
1
1
  import { colors, type ThemeName, type ColorScheme } from './colors';
2
2
  import { typography } from './typography';
3
3
  import { shadows } from './shadows';
4
+ import type { CustomColors } from '../config/appDataConfig';
4
5
 
5
6
  // Spacing scale
6
7
  export const spacing = {
@@ -32,14 +33,35 @@ export interface Theme {
32
33
  borderRadius: typeof borderRadius;
33
34
  }
34
35
 
35
- // Theme factory function
36
- export const createTheme = (themeName: ThemeName = 'primary'): Theme => ({
37
- colors: colors[themeName] as ColorScheme,
38
- typography,
39
- shadows,
40
- spacing,
41
- borderRadius,
42
- });
36
+ /**
37
+ * Theme factory function.
38
+ * If customColors are provided, they override the corresponding colors from the base theme.
39
+ */
40
+ export const createTheme = (themeName: ThemeName = 'primary', customColors?: CustomColors | null): Theme => {
41
+ const baseColors = colors[themeName] as ColorScheme;
42
+
43
+ // Merge custom color overrides on top of the base theme colors
44
+ const mergedColors = customColors
45
+ ? { ...baseColors, ...filterDefinedValues(customColors) } as ColorScheme
46
+ : baseColors;
47
+
48
+ return {
49
+ colors: mergedColors,
50
+ typography,
51
+ shadows,
52
+ spacing,
53
+ borderRadius,
54
+ };
55
+ };
56
+
57
+ /**
58
+ * Utility to filter out undefined/null values so only explicitly set colors override the theme.
59
+ */
60
+ const filterDefinedValues = (obj: Record<string, any>): Record<string, any> => {
61
+ return Object.fromEntries(
62
+ Object.entries(obj).filter(([_, value]) => value !== undefined && value !== null)
63
+ );
64
+ };
43
65
 
44
66
  // Helper function to get border with opacity
45
67
  export const getBorderColor = (theme: Theme, opacity: number = 0.36) => {
@@ -0,0 +1,40 @@
1
+ export interface SSEEvent {
2
+ eventType: string;
3
+ data: string;
4
+ }
5
+
6
+ export function parseSSEBuffer(
7
+ buffer: { value: string },
8
+ chunk: string
9
+ ): SSEEvent[] {
10
+ const results: SSEEvent[] = [];
11
+
12
+ buffer.value += chunk;
13
+
14
+ let idx: number;
15
+
16
+ while ((idx = buffer.value.indexOf('\n\n')) >= 0) {
17
+
18
+ const block = buffer.value.slice(0, idx);
19
+
20
+ buffer.value = buffer.value.slice(idx + 2);
21
+
22
+ let eventType = 'message';
23
+
24
+ const dataparts: string[] = [];
25
+
26
+ for (const line of block.split(/\r?\n/)) {
27
+ if (line.startsWith('event:')) {
28
+ eventType = line.slice(6).trim();
29
+ } else if (line.startsWith('data:')) {
30
+ dataparts.push(line.slice(5).trim());
31
+ }
32
+ }
33
+
34
+ const data = dataparts.join('\n').trim();
35
+ if (data) {
36
+ results.push({ eventType, data });
37
+ }
38
+ }
39
+ return results;
40
+ }