@transfergratis/react-native-sdk 0.1.24 → 0.1.26

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 (182) hide show
  1. package/android/build/intermediates/aapt_friendly_merged_manifests/debug/processDebugManifest/aapt/AndroidManifest.xml +12 -5
  2. package/android/build/intermediates/aar_main_jar/debug/syncDebugLibJars/classes.jar +0 -0
  3. package/android/build/intermediates/annotations_typedef_file/debug/extractDebugAnnotations/typedefs.txt +0 -0
  4. package/android/build/intermediates/incremental/debug/packageDebugResources/compile-file-map.properties +1 -1
  5. package/android/build/intermediates/incremental/debug-mergeJavaRes/merge-state +0 -0
  6. package/android/build/intermediates/manifest_merge_blame_file/debug/processDebugManifest/manifest-merger-blame-debug-report.txt +61 -59
  7. package/android/build/intermediates/merged_java_res/debug/mergeDebugJavaResource/feature-transfergratis-react-native-sdk.jar +0 -0
  8. package/android/build/intermediates/merged_manifest/debug/processDebugManifest/AndroidManifest.xml +12 -5
  9. package/android/build/kotlin/compileDebugKotlin/cacheable/last-build.bin +0 -0
  10. package/android/build/kotlin/compileDebugKotlin/local-state/build-history.bin +0 -0
  11. package/android/build/outputs/aar/transfergratis-react-native-sdk-debug.aar +0 -0
  12. package/android/build/outputs/logs/manifest-merger-debug-report.txt +26 -34
  13. package/android/src/main/AndroidManifest.xml +22 -7
  14. package/build/components/EnhancedCameraView.web.d.ts.map +1 -1
  15. package/build/components/EnhancedCameraView.web.js +76 -21
  16. package/build/components/EnhancedCameraView.web.js.map +1 -1
  17. package/build/components/KYCElements/AdditionalDocumentsTemplate.d.ts +12 -0
  18. package/build/components/KYCElements/AdditionalDocumentsTemplate.d.ts.map +1 -0
  19. package/build/components/KYCElements/AdditionalDocumentsTemplate.js +283 -0
  20. package/build/components/KYCElements/AdditionalDocumentsTemplate.js.map +1 -0
  21. package/build/components/KYCElements/EmailVerificationTemplate.d.ts +12 -0
  22. package/build/components/KYCElements/EmailVerificationTemplate.d.ts.map +1 -0
  23. package/build/components/KYCElements/EmailVerificationTemplate.js +212 -0
  24. package/build/components/KYCElements/EmailVerificationTemplate.js.map +1 -0
  25. package/build/components/KYCElements/IDCardCapture.d.ts.map +1 -1
  26. package/build/components/KYCElements/IDCardCapture.js +216 -14
  27. package/build/components/KYCElements/IDCardCapture.js.map +1 -1
  28. package/build/components/KYCElements/OrientationVideoCapture.d.ts +2 -0
  29. package/build/components/KYCElements/OrientationVideoCapture.d.ts.map +1 -1
  30. package/build/components/KYCElements/OrientationVideoCapture.js +2 -2
  31. package/build/components/KYCElements/OrientationVideoCapture.js.map +1 -1
  32. package/build/components/KYCElements/OrientationVideoCaptureEnhanced.d.ts +2 -0
  33. package/build/components/KYCElements/OrientationVideoCaptureEnhanced.d.ts.map +1 -1
  34. package/build/components/KYCElements/OrientationVideoCaptureEnhanced.js +2 -2
  35. package/build/components/KYCElements/OrientationVideoCaptureEnhanced.js.map +1 -1
  36. package/build/components/KYCElements/OrientationVideoCaptureFinal.d.ts +2 -0
  37. package/build/components/KYCElements/OrientationVideoCaptureFinal.d.ts.map +1 -1
  38. package/build/components/KYCElements/OrientationVideoCaptureFinal.js +2 -2
  39. package/build/components/KYCElements/OrientationVideoCaptureFinal.js.map +1 -1
  40. package/build/components/KYCElements/PersonalInformationTemplate.d.ts +12 -0
  41. package/build/components/KYCElements/PersonalInformationTemplate.d.ts.map +1 -0
  42. package/build/components/KYCElements/PersonalInformationTemplate.js +120 -0
  43. package/build/components/KYCElements/PersonalInformationTemplate.js.map +1 -0
  44. package/build/components/KYCElements/PhoneVerificationTemplate.d.ts +12 -0
  45. package/build/components/KYCElements/PhoneVerificationTemplate.d.ts.map +1 -0
  46. package/build/components/KYCElements/PhoneVerificationTemplate.js +185 -0
  47. package/build/components/KYCElements/PhoneVerificationTemplate.js.map +1 -0
  48. package/build/components/KYCElements/SelfieCaptureTemplate.d.ts.map +1 -1
  49. package/build/components/KYCElements/SelfieCaptureTemplate.js +7 -3
  50. package/build/components/KYCElements/SelfieCaptureTemplate.js.map +1 -1
  51. package/build/components/KYCElements/WelcomeTemplate.js +2 -1
  52. package/build/components/KYCElements/WelcomeTemplate.js.map +1 -1
  53. package/build/components/OverLay/type.d.ts +2 -0
  54. package/build/components/OverLay/type.d.ts.map +1 -1
  55. package/build/components/OverLay/type.js.map +1 -1
  56. package/build/components/TemplateKYCExample.d.ts +10 -0
  57. package/build/components/TemplateKYCExample.d.ts.map +1 -1
  58. package/build/components/TemplateKYCExample.js +7 -30
  59. package/build/components/TemplateKYCExample.js.map +1 -1
  60. package/build/components/TemplateKYCFlowRefactored.d.ts +12 -0
  61. package/build/components/TemplateKYCFlowRefactored.d.ts.map +1 -1
  62. package/build/components/TemplateKYCFlowRefactored.js +25 -3
  63. package/build/components/TemplateKYCFlowRefactored.js.map +1 -1
  64. package/build/config/KYCConfig.d.ts +14 -0
  65. package/build/config/KYCConfig.d.ts.map +1 -0
  66. package/build/config/KYCConfig.js +26 -0
  67. package/build/config/KYCConfig.js.map +1 -0
  68. package/build/config/allowedDomains.d.ts.map +1 -1
  69. package/build/config/allowedDomains.js +4 -19
  70. package/build/config/allowedDomains.js.map +1 -1
  71. package/build/hooks/useOrientationVideo.d.ts +2 -1
  72. package/build/hooks/useOrientationVideo.d.ts.map +1 -1
  73. package/build/hooks/useOrientationVideo.js +3 -3
  74. package/build/hooks/useOrientationVideo.js.map +1 -1
  75. package/build/hooks/useTemplateKYCFlow.d.ts +18 -1
  76. package/build/hooks/useTemplateKYCFlow.d.ts.map +1 -1
  77. package/build/hooks/useTemplateKYCFlow.js +410 -56
  78. package/build/hooks/useTemplateKYCFlow.js.map +1 -1
  79. package/build/i18n/en/index.d.ts +42 -0
  80. package/build/i18n/en/index.d.ts.map +1 -1
  81. package/build/i18n/en/index.js +44 -2
  82. package/build/i18n/en/index.js.map +1 -1
  83. package/build/i18n/fr/index.d.ts +28 -0
  84. package/build/i18n/fr/index.d.ts.map +1 -1
  85. package/build/i18n/fr/index.js +30 -2
  86. package/build/i18n/fr/index.js.map +1 -1
  87. package/build/i18n/types.d.ts +2 -0
  88. package/build/i18n/types.d.ts.map +1 -1
  89. package/build/i18n/types.js.map +1 -1
  90. package/build/index.d.ts +1 -0
  91. package/build/index.d.ts.map +1 -1
  92. package/build/index.js +2 -0
  93. package/build/index.js.map +1 -1
  94. package/build/modules/api/CardAuthentification.d.ts +24 -3
  95. package/build/modules/api/CardAuthentification.d.ts.map +1 -1
  96. package/build/modules/api/CardAuthentification.js +90 -12
  97. package/build/modules/api/CardAuthentification.js.map +1 -1
  98. package/build/modules/api/KYCService.d.ts +17 -7
  99. package/build/modules/api/KYCService.d.ts.map +1 -1
  100. package/build/modules/api/KYCService.js +125 -37
  101. package/build/modules/api/KYCService.js.map +1 -1
  102. package/build/modules/api/SelfieVerification.d.ts +3 -1
  103. package/build/modules/api/SelfieVerification.d.ts.map +1 -1
  104. package/build/modules/api/SelfieVerification.js +17 -1
  105. package/build/modules/api/SelfieVerification.js.map +1 -1
  106. package/build/modules/api/TemplateService.d.ts +0 -1
  107. package/build/modules/api/TemplateService.d.ts.map +1 -1
  108. package/build/modules/api/TemplateService.js +3 -3
  109. package/build/modules/api/TemplateService.js.map +1 -1
  110. package/build/modules/camera/VisionCameraModule.web.d.ts.map +1 -1
  111. package/build/modules/camera/VisionCameraModule.web.js +27 -8
  112. package/build/modules/camera/VisionCameraModule.web.js.map +1 -1
  113. package/build/types/KYC.types.d.ts +130 -5
  114. package/build/types/KYC.types.d.ts.map +1 -1
  115. package/build/types/KYC.types.js.map +1 -1
  116. package/build/types/env.types.d.ts +13 -0
  117. package/build/types/env.types.d.ts.map +1 -0
  118. package/build/types/env.types.js +2 -0
  119. package/build/types/env.types.js.map +1 -0
  120. package/build/utils/cropByObb.d.ts +7 -0
  121. package/build/utils/cropByObb.d.ts.map +1 -1
  122. package/build/utils/cropByObb.js +20 -1
  123. package/build/utils/cropByObb.js.map +1 -1
  124. package/build/utils/deviceDetection.d.ts +6 -0
  125. package/build/utils/deviceDetection.d.ts.map +1 -0
  126. package/build/utils/deviceDetection.js +12 -0
  127. package/build/utils/deviceDetection.js.map +1 -0
  128. package/build/utils/platformAlert.d.ts.map +1 -1
  129. package/build/utils/platformAlert.js.map +1 -1
  130. package/build/utils/template-transformer.d.ts.map +1 -1
  131. package/build/utils/template-transformer.js +12 -0
  132. package/build/utils/template-transformer.js.map +1 -1
  133. package/build/web/WebKYCEntry.d.ts.map +1 -1
  134. package/build/web/WebKYCEntry.js +88 -38
  135. package/build/web/WebKYCEntry.js.map +1 -1
  136. package/package.json +1 -1
  137. package/plugin/build/index.d.ts +1 -0
  138. package/plugin/build/index.js +3 -1
  139. package/plugin/build/withRemovePermissions.d.ts +3 -0
  140. package/plugin/build/withRemovePermissions.js +67 -0
  141. package/plugin/build/withVisionCamera.js +3 -4
  142. package/plugin/src/index.ts +2 -1
  143. package/plugin/src/withRemovePermissions.js +85 -0
  144. package/plugin/src/withRemovePermissions.ts +83 -0
  145. package/plugin/src/withVisionCamera.js +3 -4
  146. package/plugin/src/withVisionCamera.ts +3 -4
  147. package/plugin/tsconfig.tsbuildinfo +1 -1
  148. package/plugin.js +6 -1
  149. package/src/components/EnhancedCameraView.web.tsx +76 -21
  150. package/src/components/KYCElements/AdditionalDocumentsTemplate.tsx +346 -0
  151. package/src/components/KYCElements/EmailVerificationTemplate.tsx +278 -0
  152. package/src/components/KYCElements/IDCardCapture.tsx +253 -21
  153. package/src/components/KYCElements/OrientationVideoCapture.tsx +4 -1
  154. package/src/components/KYCElements/OrientationVideoCaptureEnhanced.tsx +4 -1
  155. package/src/components/KYCElements/OrientationVideoCaptureFinal.tsx +4 -1
  156. package/src/components/KYCElements/PersonalInformationTemplate.tsx +158 -0
  157. package/src/components/KYCElements/PhoneVerificationTemplate.tsx +253 -0
  158. package/src/components/KYCElements/SelfieCaptureTemplate.tsx +6 -3
  159. package/src/components/KYCElements/WelcomeTemplate.tsx +2 -1
  160. package/src/components/OverLay/type.ts +2 -0
  161. package/src/components/TemplateKYCExample.tsx +35 -46
  162. package/src/components/TemplateKYCFlowRefactored.tsx +46 -2
  163. package/src/config/KYCConfig.ts +34 -0
  164. package/src/config/allowedDomains.ts +7 -26
  165. package/src/hooks/useOrientationVideo.ts +5 -4
  166. package/src/hooks/useTemplateKYCFlow.tsx +443 -56
  167. package/src/i18n/en/index.ts +46 -3
  168. package/src/i18n/fr/index.ts +31 -2
  169. package/src/i18n/types.ts +2 -0
  170. package/src/index.ts +3 -0
  171. package/src/modules/api/CardAuthentification.ts +98 -12
  172. package/src/modules/api/KYCService.ts +158 -37
  173. package/src/modules/api/SelfieVerification.ts +25 -3
  174. package/src/modules/api/TemplateService.ts +4 -4
  175. package/src/modules/camera/VisionCameraModule.web.ts +30 -12
  176. package/src/types/KYC.types.ts +153 -6
  177. package/src/types/env.types.ts +13 -0
  178. package/src/utils/cropByObb.ts +20 -1
  179. package/src/utils/deviceDetection.ts +11 -0
  180. package/src/utils/platformAlert.ts +1 -0
  181. package/src/utils/template-transformer.ts +20 -8
  182. package/src/web/WebKYCEntry.tsx +123 -61
@@ -0,0 +1,212 @@
1
+ import React, { useState } from 'react';
2
+ import { View, Text, StyleSheet, TextInput, TouchableOpacity, Alert } from 'react-native';
3
+ import { useTemplateKYCFlowContext } from '../../hooks/useTemplateKYCFlow';
4
+ import { useI18n } from '../../hooks/useI18n';
5
+ import { Button } from '../ui/Button';
6
+ import kycService, { errorMessage } from '../../modules/api/KYCService';
7
+ /** RFC-style email validation: local@domain.tld */
8
+ const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
9
+ const isValidEmail = (value) => EMAIL_REGEX.test((value || '').trim());
10
+ export const EmailVerificationTemplate = ({ component, value, onValueChange, error: propError, }) => {
11
+ const { actions, getLocalizedText, state, apiKey } = useTemplateKYCFlowContext();
12
+ const { t } = useI18n();
13
+ const auth = apiKey ? { apiKey } : (state.session.token ? { token: state.session.token } : undefined);
14
+ // const config = component.config as EmailVerificationConfig; // Keep for future use
15
+ // State
16
+ const [step, setStep] = useState('email');
17
+ const [email, setEmail] = useState('');
18
+ const [otp, setOtp] = useState('');
19
+ const [localError, setLocalError] = useState(null);
20
+ const [isSimulating, setIsSimulating] = useState(false);
21
+ const title = getLocalizedText(component.labels);
22
+ const instructions = getLocalizedText(component.instructions);
23
+ // Determine button text based on step
24
+ const verifyButtonText = getLocalizedText(component.ui.buttonText) || t('common.verify') || 'Verify';
25
+ const sendButtonText = t('common.sendCode') || 'Send Verification Code';
26
+ const buttonText = step === 'email' ? sendButtonText : verifyButtonText;
27
+ const handleSendCode = async () => {
28
+ const trimmed = email.trim();
29
+ if (!trimmed || !isValidEmail(trimmed)) {
30
+ setLocalError(t('errors.invalidEmail') || 'Please enter a valid email address');
31
+ return;
32
+ }
33
+ setLocalError(null);
34
+ setIsSimulating(true);
35
+ try {
36
+ await kycService.sendEmailVerificationCode(trimmed, auth);
37
+ setStep('otp');
38
+ }
39
+ catch (err) {
40
+ const msg = errorMessage(err) ?? err?.message ?? (t('errors.sendCodeFailed') || 'Failed to send verification code');
41
+ setLocalError(typeof msg === 'string' ? msg : JSON.stringify(msg));
42
+ }
43
+ finally {
44
+ setIsSimulating(false);
45
+ }
46
+ };
47
+ const handleVerifyCode = async () => {
48
+ if (!otp || otp.length < 4) {
49
+ setLocalError(t('errors.invalidCode') || 'Please enter the 6-digit code');
50
+ return;
51
+ }
52
+ setLocalError(null);
53
+ setIsSimulating(true);
54
+ try {
55
+ await kycService.verifyEmailCode(otp.trim(), auth);
56
+ const data = { email, otp, verified: true };
57
+ onValueChange(data);
58
+ actions.nextComponent(data);
59
+ }
60
+ catch (err) {
61
+ const msg = errorMessage(err) ?? err?.message ?? (t('errors.wrongCode') || 'Invalid verification code');
62
+ setLocalError(typeof msg === 'string' ? msg : JSON.stringify(msg));
63
+ }
64
+ finally {
65
+ setIsSimulating(false);
66
+ }
67
+ };
68
+ const onChangeEmail = (text) => {
69
+ setEmail(text);
70
+ if (localError)
71
+ setLocalError(null);
72
+ };
73
+ const onChangeOtp = (text) => {
74
+ setOtp(text);
75
+ if (localError)
76
+ setLocalError(null);
77
+ };
78
+ const handleBackToEmail = () => {
79
+ setStep('email');
80
+ setOtp('');
81
+ setLocalError(null);
82
+ };
83
+ return (<View style={styles.container}>
84
+ <Text style={styles.title}>{title}</Text>
85
+ <Text style={styles.instructions}>
86
+ {step === 'email' ? instructions : (t('kyc.enterCodeSent') || `Please enter the code sent to ${email}`)}
87
+ </Text>
88
+
89
+ <View style={styles.contentContainer}>
90
+ {step === 'email' ? (<View style={styles.inputContainer}>
91
+ <Text style={styles.label}>{t('common.email') || 'Email'}</Text>
92
+ <TextInput style={styles.input} placeholder="name@example.com" value={email} onChangeText={onChangeEmail} keyboardType="email-address" autoCapitalize="none" autoCorrect={false} editable={!isSimulating}/>
93
+ </View>) : (<View style={styles.inputContainer}>
94
+ <Text style={styles.label}>{t('common.verificationCode') || 'Verification Code'}</Text>
95
+ <TextInput style={styles.input} placeholder="123456" value={otp} onChangeText={onChangeOtp} keyboardType="number-pad" maxLength={6} editable={!isSimulating}/>
96
+ <TouchableOpacity onPress={handleBackToEmail} style={styles.changeEmailLink}>
97
+ <Text style={styles.changeEmailText}>{t('common.changeEmail') || 'Change email'}</Text>
98
+ </TouchableOpacity>
99
+ </View>)}
100
+
101
+ {(localError || propError) && (<Text style={styles.errorText}>{localError || propError}</Text>)}
102
+
103
+ <Button title={isSimulating ? (t('common.processing') || 'Processing...') : buttonText} onPress={step === 'email' ? handleSendCode : handleVerifyCode} disabled={isSimulating ||
104
+ (step === 'email' ? !email : !otp)}/>
105
+
106
+ {step === 'otp' && (<TouchableOpacity onPress={async () => {
107
+ if (isSimulating)
108
+ return;
109
+ setLocalError(null);
110
+ setIsSimulating(true);
111
+ try {
112
+ await kycService.sendEmailVerificationCode(email.trim(), auth);
113
+ Alert.alert(t('common.codeResent') || 'Code Resent', t('common.codeResentMessage', { email }) || 'Code resent to ' + email);
114
+ }
115
+ catch (err) {
116
+ const msg = errorMessage(err) ?? err?.message ?? (t('errors.sendCodeFailed') || 'Failed to send code');
117
+ setLocalError(typeof msg === 'string' ? msg : JSON.stringify(msg));
118
+ }
119
+ finally {
120
+ setIsSimulating(false);
121
+ }
122
+ }} style={styles.resendButton} disabled={isSimulating}>
123
+ <Text style={styles.resendText}>{t('common.resendCode') || 'Resend Code'}</Text>
124
+ </TouchableOpacity>)}
125
+ </View>
126
+ </View>);
127
+ };
128
+ const styles = StyleSheet.create({
129
+ container: {
130
+ padding: 24,
131
+ backgroundColor: 'white',
132
+ borderRadius: 16,
133
+ margin: 16,
134
+ shadowColor: '#000',
135
+ shadowOffset: { width: 0, height: 4 },
136
+ shadowOpacity: 0.1,
137
+ shadowRadius: 12,
138
+ elevation: 5,
139
+ width: '95%',
140
+ },
141
+ title: {
142
+ fontSize: 24,
143
+ fontWeight: '700',
144
+ marginBottom: 8,
145
+ color: '#1a1a1a',
146
+ textAlign: 'center',
147
+ },
148
+ instructions: {
149
+ fontSize: 16,
150
+ color: '#666',
151
+ marginBottom: 32,
152
+ lineHeight: 24,
153
+ textAlign: 'center',
154
+ },
155
+ contentContainer: {
156
+ // width: '100%',
157
+ },
158
+ inputContainer: {
159
+ marginBottom: 24,
160
+ },
161
+ label: {
162
+ fontSize: 14,
163
+ fontWeight: '600',
164
+ color: '#333',
165
+ marginBottom: 8,
166
+ marginLeft: 4,
167
+ },
168
+ input: {
169
+ borderWidth: 1,
170
+ borderColor: '#e0e0e0',
171
+ padding: 16,
172
+ borderRadius: 12,
173
+ fontSize: 16,
174
+ backgroundColor: '#f8f9fa',
175
+ color: '#333',
176
+ },
177
+ errorText: {
178
+ color: '#dc2626',
179
+ marginBottom: 16,
180
+ fontSize: 14,
181
+ textAlign: 'center',
182
+ backgroundColor: '#fee2e2',
183
+ padding: 8,
184
+ borderRadius: 8,
185
+ overflow: 'hidden',
186
+ },
187
+ button: {
188
+ height: 50,
189
+ borderRadius: 12,
190
+ width: "100%"
191
+ },
192
+ changeEmailLink: {
193
+ alignSelf: 'flex-end',
194
+ marginTop: 8,
195
+ },
196
+ changeEmailText: {
197
+ color: '#2DBD60',
198
+ fontSize: 14,
199
+ fontWeight: '500',
200
+ },
201
+ resendButton: {
202
+ marginTop: 16,
203
+ alignItems: 'center',
204
+ width: "100%"
205
+ },
206
+ resendText: {
207
+ color: '#666',
208
+ fontSize: 14,
209
+ textDecorationLine: 'underline',
210
+ },
211
+ });
212
+ //# sourceMappingURL=EmailVerificationTemplate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EmailVerificationTemplate.js","sourceRoot":"","sources":["../../../src/components/KYCElements/EmailVerificationTemplate.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, { useState } from 'react';\nimport { View, Text, StyleSheet, TextInput, TouchableOpacity, Alert } from 'react-native';\nimport { TemplateComponent, LocalizedText } from '../../types/KYC.types';\nimport { useTemplateKYCFlowContext } from '../../hooks/useTemplateKYCFlow';\nimport { useI18n } from '../../hooks/useI18n';\nimport { Button } from '../ui/Button';\nimport kycService, { errorMessage } from '../../modules/api/KYCService';\n\ninterface EmailVerificationTemplateProps {\n component: TemplateComponent;\n value?: any;\n onValueChange: (data: any) => void;\n error?: string;\n language?: string;\n}\n\ntype VerificationStep = 'email' | 'otp';\n\n/** RFC-style email validation: local@domain.tld */\nconst EMAIL_REGEX = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n\nconst isValidEmail = (value: string): boolean => EMAIL_REGEX.test((value || '').trim());\n\nexport const EmailVerificationTemplate: React.FC<EmailVerificationTemplateProps> = ({\n component,\n value,\n onValueChange,\n error: propError,\n}) => {\n const { actions, getLocalizedText, state, apiKey } = useTemplateKYCFlowContext();\n const { t } = useI18n();\n\n const auth = apiKey ? { apiKey } : (state.session.token ? { token: state.session.token } : undefined);\n // const config = component.config as EmailVerificationConfig; // Keep for future use\n\n // State\n const [step, setStep] = useState<VerificationStep>('email');\n const [email, setEmail] = useState('');\n const [otp, setOtp] = useState('');\n const [localError, setLocalError] = useState<string | null>(null);\n const [isSimulating, setIsSimulating] = useState(false);\n\n const title = getLocalizedText(component.labels as LocalizedText);\n const instructions = getLocalizedText(component.instructions as LocalizedText);\n\n // Determine button text based on step\n const verifyButtonText = getLocalizedText((component.ui as any).buttonText) || t('common.verify') || 'Verify';\n const sendButtonText = t('common.sendCode') || 'Send Verification Code';\n const buttonText = step === 'email' ? sendButtonText : verifyButtonText;\n\n const handleSendCode = async () => {\n const trimmed = email.trim();\n if (!trimmed || !isValidEmail(trimmed)) {\n setLocalError(t('errors.invalidEmail') || 'Please enter a valid email address');\n return;\n }\n\n setLocalError(null);\n setIsSimulating(true);\n\n try {\n await kycService.sendEmailVerificationCode(trimmed, auth);\n setStep('otp');\n } catch (err: any) {\n const msg = errorMessage(err) ?? err?.message ?? (t('errors.sendCodeFailed') || 'Failed to send verification code');\n setLocalError(typeof msg === 'string' ? msg : JSON.stringify(msg));\n } finally {\n setIsSimulating(false);\n }\n };\n\n const handleVerifyCode = async () => {\n if (!otp || otp.length < 4) {\n setLocalError(t('errors.invalidCode') || 'Please enter the 6-digit code');\n return;\n }\n\n setLocalError(null);\n setIsSimulating(true);\n\n try {\n await kycService.verifyEmailCode(otp.trim(), auth);\n const data = { email, otp, verified: true };\n onValueChange(data);\n actions.nextComponent(data);\n } catch (err: any) {\n const msg = errorMessage(err) ?? err?.message ?? (t('errors.wrongCode') || 'Invalid verification code');\n setLocalError(typeof msg === 'string' ? msg : JSON.stringify(msg));\n } finally {\n setIsSimulating(false);\n }\n };\n\n const onChangeEmail = (text: string) => {\n setEmail(text);\n if (localError) setLocalError(null);\n };\n\n const onChangeOtp = (text: string) => {\n setOtp(text);\n if (localError) setLocalError(null);\n };\n\n const handleBackToEmail = () => {\n setStep('email');\n setOtp('');\n setLocalError(null);\n };\n\n return (\n <View style={styles.container}>\n <Text style={styles.title}>{title}</Text>\n <Text style={styles.instructions}>\n {step === 'email' ? instructions : (t('kyc.enterCodeSent') || `Please enter the code sent to ${email}`)}\n </Text>\n\n <View style={styles.contentContainer}>\n {step === 'email' ? (\n <View style={styles.inputContainer}>\n <Text style={styles.label}>{t('common.email') || 'Email'}</Text>\n <TextInput\n style={styles.input}\n placeholder=\"name@example.com\"\n value={email}\n onChangeText={onChangeEmail}\n keyboardType=\"email-address\"\n autoCapitalize=\"none\"\n autoCorrect={false}\n editable={!isSimulating}\n />\n </View>\n ) : (\n <View style={styles.inputContainer}>\n <Text style={styles.label}>{t('common.verificationCode') || 'Verification Code'}</Text>\n <TextInput\n style={styles.input}\n placeholder=\"123456\"\n value={otp}\n onChangeText={onChangeOtp}\n keyboardType=\"number-pad\"\n maxLength={6}\n editable={!isSimulating}\n />\n <TouchableOpacity onPress={handleBackToEmail} style={styles.changeEmailLink}>\n <Text style={styles.changeEmailText}>{t('common.changeEmail') || 'Change email'}</Text>\n </TouchableOpacity>\n </View>\n )}\n\n {(localError || propError) && (\n <Text style={styles.errorText}>{localError || propError}</Text>\n )}\n\n <Button\n title={isSimulating ? (t('common.processing') || 'Processing...') : buttonText}\n onPress={step === 'email' ? handleSendCode : handleVerifyCode}\n\n\n disabled={\n isSimulating ||\n (step === 'email' ? !email : !otp)\n }\n />\n\n {step === 'otp' && (\n <TouchableOpacity\n onPress={async () => {\n if (isSimulating) return;\n setLocalError(null);\n setIsSimulating(true);\n try {\n await kycService.sendEmailVerificationCode(email.trim(), auth);\n Alert.alert(\n t('common.codeResent') || 'Code Resent',\n t('common.codeResentMessage', { email }) || 'Code resent to ' + email\n );\n } catch (err: any) {\n const msg = errorMessage(err) ?? err?.message ?? (t('errors.sendCodeFailed') || 'Failed to send code');\n setLocalError(typeof msg === 'string' ? msg : JSON.stringify(msg));\n } finally {\n setIsSimulating(false);\n }\n }}\n style={styles.resendButton}\n disabled={isSimulating}\n >\n <Text style={styles.resendText}>{t('common.resendCode') || 'Resend Code'}</Text>\n </TouchableOpacity>\n )}\n </View>\n </View>\n );\n};\n\nconst styles = StyleSheet.create({\n container: {\n padding: 24,\n backgroundColor: 'white',\n borderRadius: 16,\n margin: 16,\n shadowColor: '#000',\n shadowOffset: { width: 0, height: 4 },\n shadowOpacity: 0.1,\n shadowRadius: 12,\n elevation: 5,\n width: '95%',\n },\n title: {\n fontSize: 24,\n fontWeight: '700',\n marginBottom: 8,\n color: '#1a1a1a',\n textAlign: 'center',\n },\n instructions: {\n fontSize: 16,\n color: '#666',\n marginBottom: 32,\n lineHeight: 24,\n textAlign: 'center',\n },\n contentContainer: {\n // width: '100%',\n },\n inputContainer: {\n marginBottom: 24,\n },\n label: {\n fontSize: 14,\n fontWeight: '600',\n color: '#333',\n marginBottom: 8,\n marginLeft: 4,\n },\n input: {\n borderWidth: 1,\n borderColor: '#e0e0e0',\n padding: 16,\n borderRadius: 12,\n fontSize: 16,\n backgroundColor: '#f8f9fa',\n color: '#333',\n },\n errorText: {\n color: '#dc2626',\n marginBottom: 16,\n fontSize: 14,\n textAlign: 'center',\n backgroundColor: '#fee2e2',\n padding: 8,\n borderRadius: 8,\n overflow: 'hidden',\n },\n button: {\n height: 50,\n borderRadius: 12,\n width: \"100%\"\n },\n changeEmailLink: {\n alignSelf: 'flex-end',\n marginTop: 8,\n },\n changeEmailText: {\n color: '#2DBD60',\n fontSize: 14,\n fontWeight: '500',\n },\n resendButton: {\n marginTop: 16,\n alignItems: 'center',\n width: \"100%\"\n },\n resendText: {\n color: '#666',\n fontSize: 14,\n textDecorationLine: 'underline',\n },\n});\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"IDCardCapture.d.ts","sourceRoot":"","sources":["../../../src/components/KYCElements/IDCardCapture.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAuC,MAAM,OAAO,CAAC;AAI5D,OAAO,EAAE,iBAAiB,EAAoI,MAAM,uBAAuB,CAAC;AAc5L,UAAU,cAAc;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,UAAU,kBAAkB;IAC1B,SAAS,EAAE,iBAAiB,CAAC;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IACvC,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAAC,KAAK,IAAI,CAAC;IACxE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CAyiBtD,CAAC"}
1
+ {"version":3,"file":"IDCardCapture.d.ts","sourceRoot":"","sources":["../../../src/components/KYCElements/IDCardCapture.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAuC,MAAM,OAAO,CAAC;AAI5D,OAAO,EAAE,iBAAiB,EAAoI,MAAM,uBAAuB,CAAC;AAe5L,UAAU,cAAc;IACtB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,UAAU,kBAAkB;IAC1B,SAAS,EAAE,iBAAiB,CAAC;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IACvC,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAAC,KAAK,IAAI,CAAC;IACxE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CAktBtD,CAAC"}
@@ -1,5 +1,5 @@
1
1
  import React, { useEffect, useMemo, useState } from 'react';
2
- import { View, Text, StyleSheet, Image, ScrollView } from 'react-native';
2
+ import { View, Text, StyleSheet, Image, ScrollView, Platform, Modal, TouchableOpacity, ActivityIndicator } from 'react-native';
3
3
  import { showAlert } from '../../utils/platformAlert';
4
4
  import { EnhancedCameraView } from '../EnhancedCameraView';
5
5
  import { GovernmentDocumentTypeShorted, GovernmentDocumentTypeBackend } from '../../types/KYC.types';
@@ -11,8 +11,9 @@ import { removeDuplicates } from '../../utils/remove-duplicate';
11
11
  import { backVerification, checkTemplateType, frontVerification } from '../../modules/api/CardAuthentification';
12
12
  import { getDocumentTypeInfo } from '../../utils/get-document-type-info';
13
13
  import pathToBase64 from '../../utils/pathToBase64';
14
- import { truncateFields } from '../../modules/api/KYCService';
15
- import { cropByObb, cropImageWithBBox, cropImageWithBBoxWithTolerance } from '../../utils/cropByObb';
14
+ import kycService, { truncateFields } from '../../modules/api/KYCService';
15
+ import { cropByObb, cropImageWithBBox, cropImageWithBBoxWithTolerance, getObbConfidence, OBB_CONFIDENCE_THRESHOLD } from '../../utils/cropByObb';
16
+ import { isMobileWeb } from '../../utils/deviceDetection';
16
17
  import { logger } from '../../utils/logger';
17
18
  export const IDCardCapture = ({ component, value = {}, onValueChange, error, language = 'en', }) => {
18
19
  const { t, locale } = useI18n();
@@ -23,6 +24,7 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
23
24
  // Stocker les bbox par côté pour pouvoir restaurer les images croppées
24
25
  const [bboxBySide, setBboxBySide] = useState({});
25
26
  const [silentCaptureResult, setSilentCaptureResult] = useState({ success: false, isAnalyzing: false });
27
+ const [showQRModal, setShowQRModal] = useState(false);
26
28
  // Mapping des types de documents backend vers SDK
27
29
  const documentTypeMapping = {
28
30
  'nationalId': 'national_id',
@@ -32,7 +34,7 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
32
34
  'healthInsuranceCard': 'health_insurance_card',
33
35
  };
34
36
  // const [imageNaturalSize, setImageNaturalSize] = useState<{ width: number; height: number } | null>(null);
35
- const { actions, state } = useTemplateKYCFlowContext();
37
+ const { actions, state, env } = useTemplateKYCFlowContext();
36
38
  const getLocalizedText = (text) => {
37
39
  // console.log("text", text, JSON.stringify(component, null, 2));
38
40
  if (text && typeof text[currentSide] === 'object' && text[currentSide][locale]) {
@@ -61,6 +63,33 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
61
63
  const countryData = useMemo(() => {
62
64
  return countrySelectionData;
63
65
  }, [countrySelectionData]);
66
+ // Synchroniser capturedImages avec value quand les données sont chargées (ex: reprise de session)
67
+ useEffect(() => {
68
+ if (value && Object.keys(value).length > 0) {
69
+ // Vérifier si les données ont changé
70
+ const valueChanged = JSON.stringify(value) !== JSON.stringify(capturedImages);
71
+ if (valueChanged) {
72
+ logger.log("Updating capturedImages from value:", Object.keys(value));
73
+ logger.log("Value data sample:", truncateFields(value));
74
+ const updatedImages = value;
75
+ setCapturedImages(updatedImages);
76
+ // Si on a des images chargées, mettre à jour silentCaptureResult pour l'affichage
77
+ Object.keys(updatedImages).forEach((side) => {
78
+ const imageData = updatedImages[side];
79
+ if (imageData?.dir) {
80
+ setSilentCaptureResult(prev => ({
81
+ ...prev,
82
+ path: imageData.dir,
83
+ success: true,
84
+ isAnalyzing: false,
85
+ mrz: imageData.mrz || '',
86
+ templatePath: imageData.templatePath || '',
87
+ }));
88
+ }
89
+ });
90
+ }
91
+ }
92
+ }, [value]);
64
93
  useEffect(() => {
65
94
  logger.log("cropImageUri", JSON.stringify(truncateFields({ box: silentCaptureResult }), null, 2));
66
95
  if (capturedImages[currentSide]?.dir && silentCaptureResult?.bbox) {
@@ -158,13 +187,23 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
158
187
  countrySelectionDataDocumentType: countrySelectionData?.documentType,
159
188
  docTypeToSend: selectedDocumentType?.type
160
189
  });
161
- const templateType = await checkTemplateType({ path: result.path || '', docType: selectedDocumentType?.type, docRegion: countryData?.code || "", postfix: currentSide });
190
+ const templateType = await checkTemplateType({ path: result.path || '', docType: selectedDocumentType?.type, docRegion: countryData?.code || "", postfix: currentSide }, env);
162
191
  if (templateType.template_path) {
163
192
  templatePath = templateType.template_path;
164
193
  logger.log("templatePath", templatePath);
165
194
  setSilentCaptureResult((prev) => ({ ...prev, templatePath: templatePath }));
166
195
  }
167
196
  if (templateType.card_obb) {
197
+ const obbConfidence = getObbConfidence(templateType.card_obb);
198
+ if (obbConfidence !== null && obbConfidence < OBB_CONFIDENCE_THRESHOLD) {
199
+ setSilentCaptureResult((prev) => ({
200
+ ...prev,
201
+ isAnalyzing: false,
202
+ success: false,
203
+ error: t('kyc.idCardCapture.cardNotFullyInFrame'),
204
+ }));
205
+ return;
206
+ }
168
207
  let bbox;
169
208
  try {
170
209
  const crop = await cropByObb(result?.path || '', templateType.card_obb);
@@ -206,7 +245,7 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
206
245
  };
207
246
  console.log("frontVerification params", verificationParams);
208
247
  console.log("About to call frontVerification function");
209
- const promise = frontVerification(verificationParams);
248
+ const promise = frontVerification(verificationParams, env);
210
249
  console.log("frontVerification promise created", promise);
211
250
  promise.then((mrz) => {
212
251
  logger.log("front verification result", truncateFields(mrz));
@@ -227,8 +266,9 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
227
266
  }).catch((e) => {
228
267
  console.log("error front verification", e);
229
268
  logger.log("error front verification", truncateFields(e));
230
- setSilentCaptureResult((prev) => ({ ...prev, isAnalyzing: false, templatePath: templatePath, success: false, error: e?.message || 'Erreur de détection du MRZ' }));
231
- // showAlert('Erreur', e?.message || 'Erreur de détection du MRZ');
269
+ const isCardNotFullyInFrame = e?.message?.includes('entirement') || e?.message?.includes('fully in frame');
270
+ const errorMessage = isCardNotFullyInFrame ? t('kyc.idCardCapture.cardNotFullyInFrame') : (e?.message || 'Erreur de détection du MRZ');
271
+ setSilentCaptureResult((prev) => ({ ...prev, isAnalyzing: false, templatePath: templatePath, success: false, error: errorMessage }));
232
272
  });
233
273
  }
234
274
  catch (error) {
@@ -250,7 +290,7 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
250
290
  currentSide: currentSide,
251
291
  templatePath: templatePath,
252
292
  mrzType: getCorrespondingMrzType(templatePath, backRegionMappings.regionMapping, backRegionMappings.key || '') || '',
253
- }).then((mrz) => {
293
+ }, env).then((mrz) => {
254
294
  logger.log("back verification result", truncateFields(mrz));
255
295
  const bbox = mrz?.bbox || templateBbox;
256
296
  setSilentCaptureResult((prev) => ({
@@ -265,8 +305,9 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
265
305
  }
266
306
  }).catch((e) => {
267
307
  logger.log("error back verification", truncateFields(e));
268
- setSilentCaptureResult((prev) => ({ ...prev, isAnalyzing: false, templatePath: templatePath, success: false, error: e?.message || 'Erreur de détection du MRZ' }));
269
- // showAlert('Erreur', e?.message || 'Erreur de détection du MRZ');
308
+ const isCardNotFullyInFrame = e?.message?.includes('entirement') || e?.message?.includes('fully in frame');
309
+ const errorMessage = isCardNotFullyInFrame ? t('kyc.idCardCapture.cardNotFullyInFrame') : (e?.message || 'Erreur de détection du MRZ');
310
+ setSilentCaptureResult((prev) => ({ ...prev, isAnalyzing: false, templatePath: templatePath, success: false, error: errorMessage }));
270
311
  });
271
312
  }
272
313
  }
@@ -315,6 +356,73 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
315
356
  useEffect(() => {
316
357
  actions.showCustomStepper(!showCamera);
317
358
  }, [showCamera]);
359
+ // Cross-device polling logic
360
+ useEffect(() => {
361
+ if (!showQRModal || !state.session.session_id)
362
+ return;
363
+ const pollInterval = setInterval(async () => {
364
+ try {
365
+ const result = await kycService.getVerificationResult(state.session.session_id);
366
+ const sessionData = result[state.session.session_id]?.data;
367
+ if (sessionData) {
368
+ // Check if verification is completed or if we have ID card data
369
+ // Since the requirement is "verification restarts", we might look for overall completion
370
+ // or we could check if user_data is populated.
371
+ // For now, let's assume if status is not PENDING/INITIALIZED it might be done.
372
+ // Or simplier: if the mobile flow completes, the session status updates.
373
+ logger.log('Polling result:', truncateFields(sessionData));
374
+ if (sessionData.verification_status === 'completed' || sessionData.verification_status === 'approved' || sessionData.verification_status === 'review') {
375
+ clearInterval(pollInterval);
376
+ setShowQRModal(false);
377
+ actions.submitVerification(); // Or handleComplete
378
+ }
379
+ }
380
+ }
381
+ catch (error) {
382
+ console.error('Polling error:', error);
383
+ }
384
+ }, 5000);
385
+ return () => clearInterval(pollInterval);
386
+ }, [showQRModal, state.session.session_id, actions]);
387
+ const getQrCodeUrl = () => {
388
+ // Only available on web platform
389
+ if (Platform.OS !== 'web')
390
+ return '';
391
+ if (typeof window === 'undefined' || !window.location || !window.location.href)
392
+ return '';
393
+ try {
394
+ const currentUrl = new URL(window.location.href);
395
+ if (!currentUrl.searchParams.has('kyc_id') && state.session.session_id) {
396
+ currentUrl.searchParams.set('kyc_id', state.session.session_id);
397
+ }
398
+ currentUrl.searchParams.set('component_index', String(state.currentComponentIndex));
399
+ if (countrySelectionData?.code) {
400
+ currentUrl.searchParams.set('country', countrySelectionData.code);
401
+ if (countrySelectionData.documentType)
402
+ currentUrl.searchParams.set('document_type', countrySelectionData.documentType);
403
+ if (countrySelectionData.region)
404
+ currentUrl.searchParams.set('region', countrySelectionData.region);
405
+ }
406
+ return `https://api.qrserver.com/v1/create-qr-code/?size=250x250&data=${encodeURIComponent(currentUrl.toString())}`;
407
+ }
408
+ catch (error) {
409
+ console.warn('Error generating QR code URL:', error);
410
+ return '';
411
+ }
412
+ };
413
+ // En reprise sur un autre appareil: afficher un chargement tant que les données de session ne sont pas restaurées
414
+ const isResumingSession = Boolean(state.session.session_id && state.currentComponentIndex > 0);
415
+ const sessionDataRestored = state.session.sessionDataRestored !== false;
416
+ if (isResumingSession && !sessionDataRestored && (!countrySelectionData || !selectedDocumentType)) {
417
+ return (<View style={styles.root}>
418
+ <View style={[styles.container, { justifyContent: 'center', alignItems: 'center' }]}>
419
+ <ActivityIndicator size="large" color="#2DBD60"/>
420
+ <Text style={[styles.description, { marginTop: 16 }]}>
421
+ {state.currentLanguage === 'en' ? 'Loading your session...' : 'Chargement de votre session...'}
422
+ </Text>
423
+ </View>
424
+ </View>);
425
+ }
318
426
  // Vérifier si les données sont disponibles, sinon afficher un message d'erreur
319
427
  if (!countrySelectionData || !selectedDocumentType) {
320
428
  return (<View style={styles.root}>
@@ -331,7 +439,7 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
331
439
  }
332
440
  if (showCamera) {
333
441
  return (<View style={styles.cameraContainer}>
334
- <EnhancedCameraView showCamera={true} cameraType={cameraConfig.cameraType} style={styles.camera} onCapture={handleCapture} onError={handleError} onClose={() => setShowCamera(false)} quality="high" showCaptureButton={true} showSwitchCamera={true} onSilentCapture={handleSilentCapture} silentCaptureResult={silentCaptureResult} enableFlash={cameraConfig.flashMode === 'auto' || cameraConfig.flashMode === 'on'} overlayComponent={<IdCardOverlay xMin={cameraConfig.overlay.bbox.xMin} yMin={cameraConfig.overlay.bbox.yMin} xMax={cameraConfig.overlay.bbox.xMax} yMax={cameraConfig.overlay.bbox.yMax} instructions={cameraConfig.overlay.guideText} cornerOpacity={cameraConfig.overlay.bbox.cornerRadius || 0} isSuccess={silentCaptureResult.success} language={state.currentLanguage} stepperProps={{
442
+ <EnhancedCameraView showCamera={true} cameraType={cameraConfig.cameraType} style={styles.camera} onCapture={handleCapture} onError={handleError} onClose={() => setShowCamera(false)} quality="high" showCaptureButton={true} showSwitchCamera={true} onSilentCapture={handleSilentCapture} silentCaptureResult={silentCaptureResult} captureStabilizationDelayMs={3000} enableFlash={cameraConfig.flashMode === 'auto' || cameraConfig.flashMode === 'on'} overlayComponent={<IdCardOverlay xMin={cameraConfig.overlay.bbox.xMin} yMin={cameraConfig.overlay.bbox.yMin} xMax={cameraConfig.overlay.bbox.xMax} yMax={cameraConfig.overlay.bbox.yMax} instructions={cameraConfig.overlay.guideText} cornerOpacity={cameraConfig.overlay.bbox.cornerRadius || 0} isSuccess={silentCaptureResult.success} language={state.currentLanguage} stepperProps={{
335
443
  back: () => {
336
444
  if (currentSide === 'back') {
337
445
  setCurrentSide('front');
@@ -404,7 +512,13 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
404
512
  borderRadius: 12,
405
513
  resizeMode: 'cover',
406
514
  }}/>) : null}
407
- {silentCaptureResult.path ? (<Image source={{ uri: silentCaptureResult.path }} style={{
515
+ {!cropImageUri && silentCaptureResult.path ? (<Image source={{ uri: silentCaptureResult.path }} style={{
516
+ width: '100%',
517
+ height: 200,
518
+ borderRadius: 12,
519
+ resizeMode: 'cover',
520
+ }}/>) : null}
521
+ {!cropImageUri && !silentCaptureResult.path && capturedImages[currentSide]?.dir ? (<Image source={{ uri: capturedImages[currentSide].dir }} style={{
408
522
  width: '100%',
409
523
  height: 200,
410
524
  borderRadius: 12,
@@ -446,6 +560,32 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
446
560
 
447
561
 
448
562
  {error && (<Text style={styles.errorText}>{error}</Text>)}
563
+
564
+ {/* Cross-Device / Continue on Phone Button (Web Only) */}
565
+ {Platform.OS === 'web' && !isMobileWeb() && !capturedImages[currentSide]?.dir && (<Button title={t('kyc.idCardCapture.continueOnPhone')} onPress={() => { setShowQRModal(true); }}/>)}
566
+
567
+ {/* QR Code Modal - Web Only */}
568
+ {Platform.OS === 'web' && (<Modal visible={showQRModal} transparent={true} animationType="fade" onRequestClose={() => setShowQRModal(false)}>
569
+ <View style={styles.modalOverlay}>
570
+ <View style={styles.modalContent}>
571
+ <Text style={styles.modalTitle}>
572
+ {t('kyc.idCardCapture.continueOnMobile')}
573
+ </Text>
574
+ <Text style={styles.modalDescription}>
575
+ {t('kyc.idCardCapture.scanQrCode')}
576
+ </Text>
577
+
578
+ {showQRModal && getQrCodeUrl() ? (<Image source={{ uri: getQrCodeUrl() }} style={styles.qrCodeImage}/>) : null}
579
+
580
+ <TouchableOpacity style={styles.closeButton} onPress={() => setShowQRModal(false)}>
581
+ <Text style={styles.closeButtonText}>
582
+ {t('common.close')}
583
+ </Text>
584
+ </TouchableOpacity>
585
+ </View>
586
+ </View>
587
+ </Modal>)}
588
+
449
589
  </View>
450
590
  </View>);
451
591
  };
@@ -475,7 +615,7 @@ const styles = StyleSheet.create({
475
615
  height: '100%',
476
616
  },
477
617
  previewContainer: {
478
- width: '100%',
618
+ width: '95%',
479
619
  backgroundColor: 'white',
480
620
  margin: 10,
481
621
  borderRadius: 10,
@@ -599,5 +739,67 @@ const styles = StyleSheet.create({
599
739
  marginTop: 8,
600
740
  textAlign: 'center',
601
741
  },
742
+ crossDeviceButton: {
743
+ marginTop: 16,
744
+ padding: 12,
745
+ alignItems: 'center',
746
+ borderWidth: 1,
747
+ borderColor: '#2DBD60',
748
+ borderRadius: 8,
749
+ backgroundColor: '#f0f9f0',
750
+ },
751
+ crossDeviceText: {
752
+ color: '#2DBD60',
753
+ fontSize: 16,
754
+ fontWeight: '600',
755
+ },
756
+ modalOverlay: {
757
+ flex: 1,
758
+ backgroundColor: 'rgba(0,0,0,0.5)',
759
+ justifyContent: 'center',
760
+ alignItems: 'center',
761
+ },
762
+ modalContent: {
763
+ backgroundColor: 'white',
764
+ borderRadius: 16,
765
+ padding: 24,
766
+ alignItems: 'center',
767
+ width: '90%',
768
+ maxWidth: 340,
769
+ shadowColor: '#000',
770
+ shadowOffset: { width: 0, height: 2 },
771
+ shadowOpacity: 0.25,
772
+ shadowRadius: 4,
773
+ elevation: 5,
774
+ },
775
+ modalTitle: {
776
+ fontSize: 20,
777
+ fontWeight: 'bold',
778
+ marginBottom: 12,
779
+ color: '#333',
780
+ },
781
+ modalDescription: {
782
+ fontSize: 16,
783
+ color: '#666',
784
+ textAlign: 'center',
785
+ marginBottom: 20,
786
+ lineHeight: 22,
787
+ },
788
+ qrCodeImage: {
789
+ width: 200,
790
+ height: 200,
791
+ marginBottom: 20,
792
+ },
793
+ closeButton: {
794
+ paddingVertical: 10,
795
+ paddingHorizontal: 20,
796
+ backgroundColor: '#f5f5f5',
797
+ borderRadius: 8,
798
+ },
799
+ closeButtonText: {
800
+ fontSize: 16,
801
+ color: '#666',
802
+ fontWeight: '600',
803
+ },
602
804
  });
603
805
  //# sourceMappingURL=IDCardCapture.js.map