@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
|
@@ -140,7 +140,8 @@ const NomineeDetail = ({ onGoBack, onSave, initialData }) => {
|
|
|
140
140
|
const month = parseInt(dateParts[1], 10);
|
|
141
141
|
const year = parseInt(dateParts[2], 10);
|
|
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
|
}
|
|
@@ -262,7 +263,8 @@ const NomineeDetail = ({ onGoBack, onSave, initialData }) => {
|
|
|
262
263
|
const year = parseInt(dateParts[2], 10);
|
|
263
264
|
// Validate the parsed values
|
|
264
265
|
if (day >= 1 && day <= 31 && month >= 1 && month <= 12 && year >= 1900 && year <= new Date().getFullYear()) {
|
|
265
|
-
|
|
266
|
+
// Use noon to avoid timezone issues
|
|
267
|
+
const parsedDate = new Date(year, month - 1, day, 12, 0, 0);
|
|
266
268
|
if (!isNaN(parsedDate.getTime()) && parsedDate.getDate() === day && parsedDate.getMonth() === month - 1 && parsedDate.getFullYear() === year) {
|
|
267
269
|
setSelectedDate(parsedDate);
|
|
268
270
|
}
|
|
@@ -297,11 +299,13 @@ const NomineeDetail = ({ onGoBack, onSave, initialData }) => {
|
|
|
297
299
|
if (react_native_1.Platform.OS === 'android') {
|
|
298
300
|
// Android fires with types: 'set' and 'dismissed'
|
|
299
301
|
if ((event === null || event === void 0 ? void 0 : event.type) === 'set' && selectedDate) {
|
|
300
|
-
|
|
302
|
+
// Normalize to noon to avoid timezone issues
|
|
303
|
+
const normalizedDate = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), selectedDate.getDate(), 12, 0, 0);
|
|
304
|
+
setSelectedDate(normalizedDate);
|
|
301
305
|
const today = new Date();
|
|
302
|
-
let age = today.getFullYear() -
|
|
303
|
-
const m = today.getMonth() -
|
|
304
|
-
if (m < 0 || (m === 0 && today.getDate() <
|
|
306
|
+
let age = today.getFullYear() - normalizedDate.getFullYear();
|
|
307
|
+
const m = today.getMonth() - normalizedDate.getMonth();
|
|
308
|
+
if (m < 0 || (m === 0 && today.getDate() < normalizedDate.getDate())) {
|
|
305
309
|
age--;
|
|
306
310
|
}
|
|
307
311
|
if (age < 18) {
|
|
@@ -310,9 +314,9 @@ const NomineeDetail = ({ onGoBack, onSave, initialData }) => {
|
|
|
310
314
|
closeDatePicker();
|
|
311
315
|
return;
|
|
312
316
|
}
|
|
313
|
-
const day =
|
|
314
|
-
const month = (
|
|
315
|
-
const year =
|
|
317
|
+
const day = normalizedDate.getDate().toString().padStart(2, '0');
|
|
318
|
+
const month = (normalizedDate.getMonth() + 1).toString().padStart(2, '0');
|
|
319
|
+
const year = normalizedDate.getFullYear().toString();
|
|
316
320
|
const formattedDate = `${day}/${month}/${year}`;
|
|
317
321
|
updateField('dateOfBirth', formattedDate);
|
|
318
322
|
closeDatePicker();
|
|
@@ -326,10 +330,12 @@ const NomineeDetail = ({ onGoBack, onSave, initialData }) => {
|
|
|
326
330
|
}
|
|
327
331
|
// iOS inline/spinner handling
|
|
328
332
|
if (selectedDate) {
|
|
329
|
-
|
|
330
|
-
const
|
|
331
|
-
|
|
332
|
-
const
|
|
333
|
+
// Normalize to noon to avoid timezone issues
|
|
334
|
+
const normalizedDate = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), selectedDate.getDate(), 12, 0, 0);
|
|
335
|
+
setSelectedDate(normalizedDate);
|
|
336
|
+
const day = normalizedDate.getDate().toString().padStart(2, '0');
|
|
337
|
+
const month = (normalizedDate.getMonth() + 1).toString().padStart(2, '0');
|
|
338
|
+
const year = normalizedDate.getFullYear().toString();
|
|
333
339
|
const formattedDate = `${day}/${month}/${year}`;
|
|
334
340
|
updateField('dateOfBirth', formattedDate);
|
|
335
341
|
}
|
|
@@ -392,7 +398,7 @@ const NomineeDetail = ({ onGoBack, onSave, initialData }) => {
|
|
|
392
398
|
return;
|
|
393
399
|
closeAllMenus();
|
|
394
400
|
openDatePicker();
|
|
395
|
-
}, editable: false, keepWhiteBackground: true
|
|
401
|
+
}, editable: false, keepWhiteBackground: true }));
|
|
396
402
|
// Validation function to check if all required fields are filled (pure function - no setState)
|
|
397
403
|
const validateForm = () => {
|
|
398
404
|
// If makeNomination is "No", no other fields are required
|
|
@@ -523,7 +529,7 @@ const NomineeDetail = ({ onGoBack, onSave, initialData }) => {
|
|
|
523
529
|
renderDropdown(nominee_1.NOMINEE_STRINGS.RELATIONSHIP_LABEL, form.relationship, 'relationship', nomineeRelationOptions, 'relationship'),
|
|
524
530
|
renderDateField(nominee_1.NOMINEE_STRINGS.DATE_OF_BIRTH_LABEL, form.dateOfBirth, 'dateOfBirth'),
|
|
525
531
|
showDatePicker && (react_1.default.createElement(react_native_1.View, { style: styles.datePickerContainer },
|
|
526
|
-
react_1.default.createElement(datetimepicker_1.default, { value: selectedDate, mode: "date", display: react_native_1.Platform.OS === 'ios' ? 'spinner' : 'default', onChange: onDateChange, maximumDate: new Date(new Date().getFullYear() - 18, new Date().getMonth(), new Date().getDate()), themeVariant: "light" }))),
|
|
532
|
+
react_1.default.createElement(datetimepicker_1.default, { value: selectedDate, mode: "date", display: react_native_1.Platform.OS === 'ios' ? 'spinner' : 'default', onChange: onDateChange, minimumDate: new Date(1900, 0, 1), maximumDate: new Date(new Date().getFullYear() - 18, new Date().getMonth(), new Date().getDate()), themeVariant: "light" }))),
|
|
527
533
|
react_1.default.createElement(react_native_1.View, { style: styles.checkboxContainer },
|
|
528
534
|
react_1.default.createElement(react_native_1.TouchableOpacity, { style: styles.checkbox, onPress: () => updateField('printNameOnCertificate', !form.printNameOnCertificate) },
|
|
529
535
|
react_1.default.createElement(react_native_1.View, { style: [styles.checkboxBox, form.printNameOnCertificate && styles.checkboxChecked] }, form.printNameOnCertificate ? (react_1.default.createElement(react_native_1.Image, { source: { uri: ((0, ThemeContext_1.useTheme)().themeName === 'dark') ? base64Images_1.base64Images.checkBoxDark : base64Images_1.base64Images.filledCheckBox }, style: { width: 22, height: 22 }, resizeMode: "contain" })) : (((0, ThemeContext_1.useTheme)().themeName === 'dark') ? (react_1.default.createElement(react_native_1.Image, { source: { uri: base64Images_1.base64Images.unCheckBoxDark }, style: { width: 22, height: 22 }, resizeMode: "contain" })) : null))),
|
package/lib/screens/PayNow.js
CHANGED
|
@@ -159,7 +159,7 @@ const PayNow = ({ onGoBack, onConfirm, fdData }) => {
|
|
|
159
159
|
const backHandler = react_native_1.BackHandler.addEventListener('hardwareBackPress', onHardwareBackPress);
|
|
160
160
|
return () => backHandler.remove();
|
|
161
161
|
}, [defaultProviderId, workflowInstanceId, applicationId, entityId]);
|
|
162
|
-
return (react_1.default.createElement(SafeAreaWrapper_1.default, { includeTop: false, bottomPadding: 25, statusBarColor:
|
|
162
|
+
return (react_1.default.createElement(SafeAreaWrapper_1.default, { includeTop: false, bottomPadding: 25, statusBarColor: colors.background, statusBarStyle: themeName === 'dark' ? 'light-content' : 'dark-content' },
|
|
163
163
|
react_1.default.createElement(react_native_1.View, { style: styles.container },
|
|
164
164
|
react_1.default.createElement(react_native_1.ScrollView, { showsVerticalScrollIndicator: false, contentContainerStyle: styles.scrollContent },
|
|
165
165
|
react_1.default.createElement(react_native_1.Text, { style: styles.title }, bank_1.BANK_STRINGS.PAYMENT_DETAILS_TITLE),
|
|
@@ -196,13 +196,13 @@ const createStyles = (colors, typography, themeName) => react_native_1.StyleShee
|
|
|
196
196
|
},
|
|
197
197
|
backButton: {
|
|
198
198
|
flex: 1,
|
|
199
|
-
backgroundColor:
|
|
200
|
-
borderWidth:
|
|
201
|
-
borderColor:
|
|
202
|
-
borderRadius:
|
|
199
|
+
backgroundColor: colors.cancelButtonBg || 'transparent',
|
|
200
|
+
borderWidth: 0,
|
|
201
|
+
borderColor: 'transparent',
|
|
202
|
+
borderRadius: 10,
|
|
203
203
|
},
|
|
204
204
|
backButtonText: {
|
|
205
|
-
color:
|
|
205
|
+
color: colors.primary,
|
|
206
206
|
},
|
|
207
207
|
confirmButton: {
|
|
208
208
|
flex: 1,
|
package/lib/screens/Payment.d.ts
CHANGED
package/lib/screens/Payment.js
CHANGED
|
@@ -43,27 +43,43 @@ const SafeAreaWrapper_1 = __importDefault(require("../components/SafeAreaWrapper
|
|
|
43
43
|
const ThemeContext_1 = require("../theme/ThemeContext");
|
|
44
44
|
const encryption_1 = require("../utils/encryption");
|
|
45
45
|
const encryptionConfig_1 = require("../config/encryptionConfig");
|
|
46
|
-
const
|
|
46
|
+
const native_1 = require("@react-navigation/native");
|
|
47
|
+
const paymentSession_1 = require("../state/paymentSession");
|
|
48
|
+
const usePaymentStatusTimer_1 = require("../hooks/usePaymentStatusTimer");
|
|
49
|
+
const Payment = ({ onGoBack, onPaymentSuccess, onPaymentFailure, onPaymentPending, paymentUrl, successUrl = 'payment/success', failureUrl = 'payment/failure', }) => {
|
|
47
50
|
const colors = (0, ThemeContext_1.useColors)();
|
|
48
51
|
const styles = createStyles(colors);
|
|
49
52
|
const webViewRef = (0, react_1.useRef)(null);
|
|
50
53
|
const [loading, setLoading] = (0, react_1.useState)(true);
|
|
54
|
+
const currentStatusRef = (0, react_1.useRef)('pending');
|
|
55
|
+
const { transactionId } = (0, paymentSession_1.getPaymentSession)();
|
|
56
|
+
const { startTimer, stopTimer } = (0, usePaymentStatusTimer_1.usePaymentStatusTimer)({
|
|
57
|
+
transactionId,
|
|
58
|
+
onStatusUpdate: (status, response) => {
|
|
59
|
+
currentStatusRef.current = status;
|
|
60
|
+
if (status === 'success') {
|
|
61
|
+
onPaymentSuccess === null || onPaymentSuccess === void 0 ? void 0 : onPaymentSuccess(response);
|
|
62
|
+
}
|
|
63
|
+
else if (status === 'failed') {
|
|
64
|
+
onPaymentFailure === null || onPaymentFailure === void 0 ? void 0 : onPaymentFailure(response);
|
|
65
|
+
}
|
|
66
|
+
else if (status === 'pending') {
|
|
67
|
+
onPaymentPending === null || onPaymentPending === void 0 ? void 0 : onPaymentPending(response);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
(0, native_1.useFocusEffect)(react_1.default.useCallback(() => {
|
|
72
|
+
startTimer();
|
|
73
|
+
}, [startTimer, stopTimer]));
|
|
51
74
|
const handleNavigationStateChange = async (navState) => {
|
|
52
75
|
const { url } = navState;
|
|
53
76
|
if (url.includes('payment/status')) {
|
|
54
77
|
await handlePaymentStatusPage();
|
|
55
78
|
return;
|
|
56
79
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
if (url.includes(failureUrl)) {
|
|
63
|
-
setLoading(false);
|
|
64
|
-
onPaymentFailure === null || onPaymentFailure === void 0 ? void 0 : onPaymentFailure(navState);
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
80
|
+
// else{
|
|
81
|
+
// onPaymentPending?.(url);
|
|
82
|
+
// }
|
|
67
83
|
};
|
|
68
84
|
const handleWebViewError = (syntheticEvent) => {
|
|
69
85
|
const { nativeEvent } = syntheticEvent;
|
|
@@ -114,17 +130,22 @@ const Payment = ({ onGoBack, onPaymentSuccess, onPaymentFailure, paymentUrl, suc
|
|
|
114
130
|
// Check payment status in data
|
|
115
131
|
const paymentStatus = (_c = (_b = response.data) === null || _b === void 0 ? void 0 : _b.paymentStatus) === null || _c === void 0 ? void 0 : _c.toLowerCase();
|
|
116
132
|
if (paymentStatus === 'success') {
|
|
133
|
+
currentStatusRef.current = "success";
|
|
134
|
+
stopTimer();
|
|
117
135
|
onPaymentSuccess === null || onPaymentSuccess === void 0 ? void 0 : onPaymentSuccess(response);
|
|
118
136
|
}
|
|
119
137
|
else if (paymentStatus === 'failed') {
|
|
138
|
+
currentStatusRef.current = "failed";
|
|
139
|
+
stopTimer();
|
|
120
140
|
onPaymentFailure === null || onPaymentFailure === void 0 ? void 0 : onPaymentFailure(response);
|
|
121
141
|
}
|
|
122
142
|
else {
|
|
123
|
-
|
|
143
|
+
onPaymentPending === null || onPaymentPending === void 0 ? void 0 : onPaymentPending(response);
|
|
124
144
|
}
|
|
125
145
|
}
|
|
126
146
|
else {
|
|
127
|
-
|
|
147
|
+
currentStatusRef.current = "pending";
|
|
148
|
+
onPaymentPending === null || onPaymentPending === void 0 ? void 0 : onPaymentPending(response);
|
|
128
149
|
}
|
|
129
150
|
setLoading(false);
|
|
130
151
|
}
|
|
@@ -54,6 +54,7 @@ const native_1 = require("@react-navigation/native");
|
|
|
54
54
|
const bank_1 = require("../constants/strings/bank");
|
|
55
55
|
const common_1 = require("../constants/strings/common");
|
|
56
56
|
const apiConfig_1 = require("../config/apiConfig");
|
|
57
|
+
const usePaymentStatusTimer_1 = require("../hooks/usePaymentStatusTimer");
|
|
57
58
|
const PaymentStatus = ({ onRetry, onContinue, status, transactionId, fdData }) => {
|
|
58
59
|
var _a;
|
|
59
60
|
const colors = (0, ThemeContext_1.useColors)();
|
|
@@ -61,7 +62,7 @@ const PaymentStatus = ({ onRetry, onContinue, status, transactionId, fdData }) =
|
|
|
61
62
|
const { themeName } = (0, ThemeContext_1.useTheme)();
|
|
62
63
|
// Local state to track current status (can be updated from API responses)
|
|
63
64
|
const [currentStatus, setCurrentStatus] = (0, react_1.useState)(status);
|
|
64
|
-
const styles = createStyles(colors, typography, currentStatus);
|
|
65
|
+
const styles = createStyles(colors, typography, currentStatus, themeName);
|
|
65
66
|
const route = (0, native_1.useRoute)();
|
|
66
67
|
// Get transaction ID from navigation props (route params)
|
|
67
68
|
const navigationTransactionId = (_a = route.params) === null || _a === void 0 ? void 0 : _a.transactionId;
|
|
@@ -74,8 +75,18 @@ const PaymentStatus = ({ onRetry, onContinue, status, transactionId, fdData }) =
|
|
|
74
75
|
const applicationId = (0, store_1.useAppSelector)((state) => { var _a; return (_a = state === null || state === void 0 ? void 0 : state.onboarding) === null || _a === void 0 ? void 0 : _a.applicationId; });
|
|
75
76
|
const entityId = (0, store_1.useAppSelector)((state) => { var _a; return (_a = state === null || state === void 0 ? void 0 : state.onboarding) === null || _a === void 0 ? void 0 : _a.entityid; });
|
|
76
77
|
const providerId = (0, store_1.useAppSelector)((state) => { var _a; return (_a = state === null || state === void 0 ? void 0 : state.onboarding) === null || _a === void 0 ? void 0 : _a.providerId; });
|
|
77
|
-
|
|
78
|
-
|
|
78
|
+
const { startTimer, stopTimer, triggerStatusCheck, isCheckingStatus, } = (0, usePaymentStatusTimer_1.usePaymentStatusTimer)({
|
|
79
|
+
transactionId: finalTransactionId,
|
|
80
|
+
overrides: {
|
|
81
|
+
providerId,
|
|
82
|
+
workflowInstanceId,
|
|
83
|
+
applicationid: applicationId,
|
|
84
|
+
entityid: entityId,
|
|
85
|
+
},
|
|
86
|
+
onStatusUpdate: (nextStatus) => {
|
|
87
|
+
setCurrentStatus(nextStatus);
|
|
88
|
+
},
|
|
89
|
+
});
|
|
79
90
|
// Payment Retry API
|
|
80
91
|
const [paymentRetry, { data: paymentRetryResponse, error: paymentRetryError, isLoading: isLoadingPaymentRetry, }] = (0, fdApi_1.usePaymentRetryMutation)();
|
|
81
92
|
// Get Customer Applications API
|
|
@@ -99,37 +110,19 @@ const PaymentStatus = ({ onRetry, onContinue, status, transactionId, fdData }) =
|
|
|
99
110
|
const formatAmount = (amount) => {
|
|
100
111
|
return amount.toLocaleString('en-IN');
|
|
101
112
|
};
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
try {
|
|
106
|
-
// Get user info from app data
|
|
107
|
-
const userInfo = (0, appDataConfig_1.getUserInfoForAPI)();
|
|
108
|
-
// Prepare the API request
|
|
109
|
-
const paymentReverseFeedRequest = {
|
|
110
|
-
// Headers
|
|
111
|
-
providerId: providerId,
|
|
112
|
-
workflowInstanceId: workflowInstanceId,
|
|
113
|
-
userreferenceid: userInfo.id,
|
|
114
|
-
applicationid: applicationId,
|
|
115
|
-
entityid: entityId,
|
|
116
|
-
// Body
|
|
117
|
-
transactionId: finalTransactionId,
|
|
118
|
-
};
|
|
119
|
-
const response = await paymentReverseFeed(paymentReverseFeedRequest).unwrap();
|
|
120
|
-
// Handle the response based on payment status
|
|
121
|
-
if (((_b = (_a = response === null || response === void 0 ? void 0 : response.data) === null || _a === void 0 ? void 0 : _a.paymentStatus) === null || _b === void 0 ? void 0 : _b.toLowerCase()) === 'success') {
|
|
122
|
-
setCurrentStatus('success');
|
|
123
|
-
}
|
|
124
|
-
else if (((_d = (_c = response === null || response === void 0 ? void 0 : response.data) === null || _c === void 0 ? void 0 : _c.paymentStatus) === null || _d === void 0 ? void 0 : _d.toLowerCase()) === 'failed') {
|
|
125
|
-
setCurrentStatus('failed');
|
|
126
|
-
}
|
|
113
|
+
(0, react_1.useEffect)(() => {
|
|
114
|
+
if (currentStatus === 'pending') {
|
|
115
|
+
startTimer();
|
|
127
116
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
// { text: COMMON_STRINGS.OK }
|
|
131
|
-
// ]);
|
|
117
|
+
else {
|
|
118
|
+
stopTimer();
|
|
132
119
|
}
|
|
120
|
+
return () => {
|
|
121
|
+
stopTimer();
|
|
122
|
+
};
|
|
123
|
+
}, [currentStatus, startTimer, stopTimer]);
|
|
124
|
+
const handlePaymentReverseFeed = async () => {
|
|
125
|
+
await triggerStatusCheck();
|
|
133
126
|
};
|
|
134
127
|
// Handle payment retry when status is failed
|
|
135
128
|
const handlePaymentRetry = async () => {
|
|
@@ -278,8 +271,8 @@ const PaymentStatus = ({ onRetry, onContinue, status, transactionId, fdData }) =
|
|
|
278
271
|
const backHandler = react_native_1.BackHandler.addEventListener('hardwareBackPress', onBackPress);
|
|
279
272
|
return () => backHandler.remove();
|
|
280
273
|
}, []);
|
|
281
|
-
return (react_1.default.createElement(SafeAreaWrapper_1.default, { includeTop: true, bottomPadding: 0, statusBarColor:
|
|
282
|
-
react_native_1.Platform.OS === 'ios' && react_1.default.createElement(react_native_1.StatusBar, { barStyle:
|
|
274
|
+
return (react_1.default.createElement(SafeAreaWrapper_1.default, { includeTop: true, bottomPadding: 0, statusBarColor: colors.background, statusBarStyle: themeName === 'dark' ? 'light-content' : 'dark-content' },
|
|
275
|
+
react_native_1.Platform.OS === 'ios' && react_1.default.createElement(react_native_1.StatusBar, { barStyle: themeName === 'dark' ? 'light-content' : 'dark-content' }),
|
|
283
276
|
react_1.default.createElement(react_native_1.View, { style: styles.container },
|
|
284
277
|
react_1.default.createElement(react_native_1.ScrollView, { showsVerticalScrollIndicator: false, contentContainerStyle: styles.scrollContent },
|
|
285
278
|
react_1.default.createElement(react_native_1.View, { style: styles.iconContainer },
|
|
@@ -301,12 +294,10 @@ const PaymentStatus = ({ onRetry, onContinue, status, transactionId, fdData }) =
|
|
|
301
294
|
react_1.default.createElement(react_native_1.Text, { style: styles.disclaimerText }, bank_1.BANK_STRINGS.PAYMENT_SUCCESS_DISCLAIMER)))),
|
|
302
295
|
react_1.default.createElement(react_native_1.View, { style: styles.footer },
|
|
303
296
|
currentStatus === 'success' && (react_1.default.createElement(ActionButton_1.default, { title: isLoadingCustomerApplications ? common_1.COMMON_STRINGS.LOADING : common_1.COMMON_STRINGS.EXIT, onPress: handleExit, disabled: isLoadingCustomerApplications })),
|
|
304
|
-
currentStatus === 'failed' && (react_1.default.createElement(ActionButton_1.default, { title: isLoadingPaymentRetry ? common_1.COMMON_STRINGS.RETRYING : bank_1.BANK_STRINGS.RETRY_PAYMENT_BUTTON, onPress: handlePaymentRetry, disabled: isLoadingPaymentRetry }))
|
|
305
|
-
currentStatus === 'pending' && (react_1.default.createElement(ActionButton_1.default, { title: isLoadingPaymentReverseFeed ? common_1.COMMON_STRINGS.CHECKING : bank_1.BANK_STRINGS.REFRESH_STATUS_BUTTON, onPress: handlePaymentReverseFeed, disabled: isLoadingPaymentReverseFeed }))))));
|
|
297
|
+
currentStatus === 'failed' && (react_1.default.createElement(ActionButton_1.default, { title: isLoadingPaymentRetry ? common_1.COMMON_STRINGS.RETRYING : bank_1.BANK_STRINGS.RETRY_PAYMENT_BUTTON, onPress: handlePaymentRetry, disabled: isLoadingPaymentRetry }))))));
|
|
306
298
|
};
|
|
307
|
-
const createStyles = (colors, typography, status) => {
|
|
308
|
-
const
|
|
309
|
-
const cardBgColor = status === 'success' ? '#E8F5F0' : status === 'failed' ? '#FFF3F3' : '#FFF8E1';
|
|
299
|
+
const createStyles = (colors, typography, status, themeName) => {
|
|
300
|
+
const isDark = themeName === 'dark';
|
|
310
301
|
return react_native_1.StyleSheet.create({
|
|
311
302
|
container: {
|
|
312
303
|
flex: 1,
|
|
@@ -342,7 +333,7 @@ const createStyles = (colors, typography, status) => {
|
|
|
342
333
|
width: 60,
|
|
343
334
|
height: 60,
|
|
344
335
|
borderRadius: 30,
|
|
345
|
-
backgroundColor: status === 'success' ? '#ffffff' : status === 'failed' ? 'rgba(244, 67, 54, 0.0)' : 'rgba(255, 152, 0, 0.2)',
|
|
336
|
+
backgroundColor: status === 'success' ? (isDark ? colors.surface : '#ffffff') : status === 'failed' ? 'rgba(244, 67, 54, 0.0)' : 'rgba(255, 152, 0, 0.2)',
|
|
346
337
|
alignItems: 'center',
|
|
347
338
|
justifyContent: 'center',
|
|
348
339
|
},
|
|
@@ -359,7 +350,7 @@ const createStyles = (colors, typography, status) => {
|
|
|
359
350
|
width: 56,
|
|
360
351
|
height: 56,
|
|
361
352
|
borderRadius: 28,
|
|
362
|
-
backgroundColor:
|
|
353
|
+
backgroundColor: colors.primary,
|
|
363
354
|
alignItems: 'center',
|
|
364
355
|
justifyContent: 'center',
|
|
365
356
|
borderWidth: 4,
|
|
@@ -368,7 +359,7 @@ const createStyles = (colors, typography, status) => {
|
|
|
368
359
|
avatarText: {
|
|
369
360
|
fontSize: 24,
|
|
370
361
|
fontWeight: '600',
|
|
371
|
-
color:
|
|
362
|
+
color: colors.headerText,
|
|
372
363
|
},
|
|
373
364
|
footer: {
|
|
374
365
|
position: 'absolute',
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React, { ReactNode } from 'react';
|
|
2
2
|
import { type Theme, type ThemeName } from './index';
|
|
3
|
+
import type { CustomColors } from '../config/appDataConfig';
|
|
3
4
|
interface ThemeContextType {
|
|
4
5
|
theme: Theme;
|
|
5
6
|
themeName: ThemeName;
|
|
@@ -9,6 +10,7 @@ interface ThemeProviderProps {
|
|
|
9
10
|
children: ReactNode;
|
|
10
11
|
initialTheme?: ThemeName;
|
|
11
12
|
theme?: Theme;
|
|
13
|
+
colorOverrides?: CustomColors | null;
|
|
12
14
|
}
|
|
13
15
|
export declare const ThemeProvider: React.FC<ThemeProviderProps>;
|
|
14
16
|
export declare const useTheme: () => ThemeContextType;
|
|
@@ -37,9 +37,11 @@ exports.useSpacing = exports.useShadows = exports.useTypography = exports.useCol
|
|
|
37
37
|
const react_1 = __importStar(require("react"));
|
|
38
38
|
const index_1 = require("./index");
|
|
39
39
|
const ThemeContext = (0, react_1.createContext)(undefined);
|
|
40
|
-
const ThemeProvider = ({ children, initialTheme = 'primary', theme: customTheme, }) => {
|
|
40
|
+
const ThemeProvider = ({ children, initialTheme = 'primary', theme: customTheme, colorOverrides, }) => {
|
|
41
41
|
const [currentThemeName, setCurrentThemeName] = react_1.default.useState(initialTheme);
|
|
42
|
-
|
|
42
|
+
// If a full custom theme is provided, use it directly.
|
|
43
|
+
// Otherwise, create theme from name and merge any color overrides on top.
|
|
44
|
+
const theme = customTheme || (0, index_1.createTheme)(currentThemeName, colorOverrides);
|
|
43
45
|
const setTheme = react_1.default.useCallback((themeName) => {
|
|
44
46
|
setCurrentThemeName(themeName);
|
|
45
47
|
}, []);
|
package/lib/theme/index.d.ts
CHANGED
|
@@ -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
|
export declare const spacing: {
|
|
5
6
|
readonly xs: 4;
|
|
6
7
|
readonly sm: 8;
|
|
@@ -25,7 +26,11 @@ export interface Theme {
|
|
|
25
26
|
spacing: typeof spacing;
|
|
26
27
|
borderRadius: typeof borderRadius;
|
|
27
28
|
}
|
|
28
|
-
|
|
29
|
+
/**
|
|
30
|
+
* Theme factory function.
|
|
31
|
+
* If customColors are provided, they override the corresponding colors from the base theme.
|
|
32
|
+
*/
|
|
33
|
+
export declare const createTheme: (themeName?: ThemeName, customColors?: CustomColors | null) => Theme;
|
|
29
34
|
export declare const getBorderColor: (theme: Theme, opacity?: number) => string;
|
|
30
35
|
export declare const getShadowColor: (opacity?: number) => string;
|
|
31
36
|
export { colors, typography, shadows, type ThemeName, type ColorScheme };
|
package/lib/theme/index.js
CHANGED
|
@@ -40,15 +40,31 @@ exports.borderRadius = {
|
|
|
40
40
|
xl: 16,
|
|
41
41
|
full: 9999,
|
|
42
42
|
};
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
43
|
+
/**
|
|
44
|
+
* Theme factory function.
|
|
45
|
+
* If customColors are provided, they override the corresponding colors from the base theme.
|
|
46
|
+
*/
|
|
47
|
+
const createTheme = (themeName = 'primary', customColors) => {
|
|
48
|
+
const baseColors = colors_1.colors[themeName];
|
|
49
|
+
// Merge custom color overrides on top of the base theme colors
|
|
50
|
+
const mergedColors = customColors
|
|
51
|
+
? Object.assign(Object.assign({}, baseColors), filterDefinedValues(customColors))
|
|
52
|
+
: baseColors;
|
|
53
|
+
return {
|
|
54
|
+
colors: mergedColors,
|
|
55
|
+
typography: typography_1.typography,
|
|
56
|
+
shadows: shadows_1.shadows,
|
|
57
|
+
spacing: exports.spacing,
|
|
58
|
+
borderRadius: exports.borderRadius,
|
|
59
|
+
};
|
|
60
|
+
};
|
|
51
61
|
exports.createTheme = createTheme;
|
|
62
|
+
/**
|
|
63
|
+
* Utility to filter out undefined/null values so only explicitly set colors override the theme.
|
|
64
|
+
*/
|
|
65
|
+
const filterDefinedValues = (obj) => {
|
|
66
|
+
return Object.fromEntries(Object.entries(obj).filter(([_, value]) => value !== undefined && value !== null));
|
|
67
|
+
};
|
|
52
68
|
// Helper function to get border with opacity
|
|
53
69
|
const getBorderColor = (theme, opacity = 0.36) => {
|
|
54
70
|
const hex = theme.colors.border.replace('#', '');
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseSSEBuffer = parseSSEBuffer;
|
|
4
|
+
function parseSSEBuffer(buffer, chunk) {
|
|
5
|
+
const results = [];
|
|
6
|
+
buffer.value += chunk;
|
|
7
|
+
let idx;
|
|
8
|
+
while ((idx = buffer.value.indexOf('\n\n')) >= 0) {
|
|
9
|
+
const block = buffer.value.slice(0, idx);
|
|
10
|
+
buffer.value = buffer.value.slice(idx + 2);
|
|
11
|
+
let eventType = 'message';
|
|
12
|
+
const dataparts = [];
|
|
13
|
+
for (const line of block.split(/\r?\n/)) {
|
|
14
|
+
if (line.startsWith('event:')) {
|
|
15
|
+
eventType = line.slice(6).trim();
|
|
16
|
+
}
|
|
17
|
+
else if (line.startsWith('data:')) {
|
|
18
|
+
dataparts.push(line.slice(5).trim());
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
const data = dataparts.join('\n').trim();
|
|
22
|
+
if (data) {
|
|
23
|
+
results.push({ eventType, data });
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return results;
|
|
27
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@finspringinnovations/fdsdk",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"description": "FD SDK for React Native applications",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
@@ -73,4 +73,4 @@
|
|
|
73
73
|
"url": "https://github.com/shriram-finance/shriramsdk/issues"
|
|
74
74
|
},
|
|
75
75
|
"license": "MIT"
|
|
76
|
-
}
|
|
76
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { View, Text, StyleSheet, Image } from 'react-native';
|
|
3
|
-
import { useColors, useTypography } from '../theme/ThemeContext';
|
|
3
|
+
import { useColors, useTypography, useTheme } from '../theme/ThemeContext';
|
|
4
4
|
import { base64Images } from '../constants/strings/base64Images';
|
|
5
5
|
|
|
6
6
|
interface CompanyHeaderProps {
|
|
@@ -11,7 +11,8 @@ interface CompanyHeaderProps {
|
|
|
11
11
|
const CompanyHeader: React.FC<CompanyHeaderProps> = ({ companyName, rating }) => {
|
|
12
12
|
const colors = useColors();
|
|
13
13
|
const typography = useTypography();
|
|
14
|
-
const
|
|
14
|
+
const { themeName } = useTheme();
|
|
15
|
+
const styles = createStyles(colors, typography, themeName);
|
|
15
16
|
|
|
16
17
|
return (
|
|
17
18
|
<View style={styles.wrapper}>
|
|
@@ -32,47 +33,52 @@ const CompanyHeader: React.FC<CompanyHeaderProps> = ({ companyName, rating }) =>
|
|
|
32
33
|
);
|
|
33
34
|
};
|
|
34
35
|
|
|
35
|
-
const createStyles = (colors: any, typography: any) =>
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
36
|
+
const createStyles = (colors: any, typography: any, themeName: string) => {
|
|
37
|
+
const isDark = themeName === 'dark';
|
|
38
|
+
|
|
39
|
+
return StyleSheet.create({
|
|
40
|
+
wrapper: {
|
|
41
|
+
paddingHorizontal: 2,
|
|
42
|
+
paddingTop: 12,
|
|
43
|
+
paddingBottom: 8,
|
|
44
|
+
},
|
|
45
|
+
card: {
|
|
46
|
+
backgroundColor: isDark ? colors.headerBg : '#FFFFFF',
|
|
47
|
+
borderRadius: 24,
|
|
48
|
+
padding: 4,
|
|
49
|
+
borderWidth: 1,
|
|
50
|
+
borderColor: isDark ? colors.headerBg + 'AA' : colors.border + '40',
|
|
51
|
+
shadowColor: colors.shadow || '#000',
|
|
52
|
+
shadowOpacity: isDark ? 0.2 : 0.1,
|
|
53
|
+
shadowOffset: { width: 0, height: 4 },
|
|
54
|
+
shadowRadius: 12,
|
|
55
|
+
elevation: 4,
|
|
56
|
+
},
|
|
57
|
+
innerBorder: {
|
|
58
|
+
borderRadius: 20,
|
|
59
|
+
padding: 16,
|
|
60
|
+
flexDirection: 'row',
|
|
61
|
+
alignItems: 'center',
|
|
62
|
+
},
|
|
63
|
+
logo: {
|
|
64
|
+
width: 45,
|
|
65
|
+
height: 45,
|
|
66
|
+
marginRight: 12,
|
|
67
|
+
},
|
|
68
|
+
info: {
|
|
69
|
+
flex: 1,
|
|
70
|
+
},
|
|
71
|
+
name: {
|
|
72
|
+
...typography.styles.h2,
|
|
73
|
+
color: isDark ? colors.headerText : colors.text,
|
|
74
|
+
marginBottom: 6,
|
|
75
|
+
},
|
|
76
|
+
rating: {
|
|
77
|
+
...typography.styles.text14Medium,
|
|
78
|
+
color: isDark ? colors.headerText : colors.primary,
|
|
79
|
+
opacity: isDark ? 0.8 : 1,
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
};
|
|
77
83
|
|
|
78
84
|
export default CompanyHeader;
|
|
@@ -42,9 +42,10 @@ const InterestRateCard: React.FC<InterestRateCardProps> = ({
|
|
|
42
42
|
const createStyles = (colors: any, typography: any, themeName: string) => StyleSheet.create({
|
|
43
43
|
card: {
|
|
44
44
|
backgroundColor: themeName === 'dark' ? colors.inputBackground : 'rgba(0,235,180,0.1)',
|
|
45
|
-
|
|
45
|
+
paddingVertical: 16,
|
|
46
46
|
borderRadius: 4,
|
|
47
47
|
marginTop: 25,
|
|
48
|
+
paddingHorizontal: 12,
|
|
48
49
|
},
|
|
49
50
|
row: {
|
|
50
51
|
flexDirection: 'row',
|
|
@@ -59,7 +60,7 @@ const createStyles = (colors: any, typography: any, themeName: string) => StyleS
|
|
|
59
60
|
},
|
|
60
61
|
label: {
|
|
61
62
|
...typography.styles.bodySmall,
|
|
62
|
-
color:
|
|
63
|
+
color: colors.textLight,
|
|
63
64
|
marginBottom: 4,
|
|
64
65
|
},
|
|
65
66
|
value: {
|