@transfergratis/react-native-sdk 0.1.23 → 0.1.25

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 (207) 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 +13 -5
  14. package/build/components/EnhancedCameraView.d.ts.map +1 -1
  15. package/build/components/EnhancedCameraView.js +26 -3
  16. package/build/components/EnhancedCameraView.js.map +1 -1
  17. package/build/components/EnhancedCameraView.web.d.ts.map +1 -1
  18. package/build/components/EnhancedCameraView.web.js +21 -0
  19. package/build/components/EnhancedCameraView.web.js.map +1 -1
  20. package/build/components/KYCElements/AdditionalDocumentsTemplate.d.ts +12 -0
  21. package/build/components/KYCElements/AdditionalDocumentsTemplate.d.ts.map +1 -0
  22. package/build/components/KYCElements/AdditionalDocumentsTemplate.js +283 -0
  23. package/build/components/KYCElements/AdditionalDocumentsTemplate.js.map +1 -0
  24. package/build/components/KYCElements/CameraCapture.d.ts.map +1 -1
  25. package/build/components/KYCElements/CameraCapture.js +4 -3
  26. package/build/components/KYCElements/CameraCapture.js.map +1 -1
  27. package/build/components/KYCElements/CountrySelectionTemplate.d.ts +5 -2
  28. package/build/components/KYCElements/CountrySelectionTemplate.d.ts.map +1 -1
  29. package/build/components/KYCElements/CountrySelectionTemplate.js +360 -101
  30. package/build/components/KYCElements/CountrySelectionTemplate.js.map +1 -1
  31. package/build/components/KYCElements/EmailVerificationTemplate.d.ts +12 -0
  32. package/build/components/KYCElements/EmailVerificationTemplate.d.ts.map +1 -0
  33. package/build/components/KYCElements/EmailVerificationTemplate.js +193 -0
  34. package/build/components/KYCElements/EmailVerificationTemplate.js.map +1 -0
  35. package/build/components/KYCElements/FileUpload.d.ts.map +1 -1
  36. package/build/components/KYCElements/FileUpload.js +5 -4
  37. package/build/components/KYCElements/FileUpload.js.map +1 -1
  38. package/build/components/KYCElements/FileUploadTemplate.d.ts.map +1 -1
  39. package/build/components/KYCElements/FileUploadTemplate.js +5 -4
  40. package/build/components/KYCElements/FileUploadTemplate.js.map +1 -1
  41. package/build/components/KYCElements/IDCardCapture.d.ts.map +1 -1
  42. package/build/components/KYCElements/IDCardCapture.js +356 -227
  43. package/build/components/KYCElements/IDCardCapture.js.map +1 -1
  44. package/build/components/KYCElements/LocationCaptureTemplate.d.ts.map +1 -1
  45. package/build/components/KYCElements/LocationCaptureTemplate.js +78 -37
  46. package/build/components/KYCElements/LocationCaptureTemplate.js.map +1 -1
  47. package/build/components/KYCElements/OrientationVideoCapture.d.ts +2 -0
  48. package/build/components/KYCElements/OrientationVideoCapture.d.ts.map +1 -1
  49. package/build/components/KYCElements/OrientationVideoCapture.js +5 -4
  50. package/build/components/KYCElements/OrientationVideoCapture.js.map +1 -1
  51. package/build/components/KYCElements/OrientationVideoCaptureEnhanced.d.ts +2 -0
  52. package/build/components/KYCElements/OrientationVideoCaptureEnhanced.d.ts.map +1 -1
  53. package/build/components/KYCElements/OrientationVideoCaptureEnhanced.js +5 -4
  54. package/build/components/KYCElements/OrientationVideoCaptureEnhanced.js.map +1 -1
  55. package/build/components/KYCElements/OrientationVideoCaptureFinal.d.ts +2 -0
  56. package/build/components/KYCElements/OrientationVideoCaptureFinal.d.ts.map +1 -1
  57. package/build/components/KYCElements/OrientationVideoCaptureFinal.js +5 -4
  58. package/build/components/KYCElements/OrientationVideoCaptureFinal.js.map +1 -1
  59. package/build/components/KYCElements/PersonalInformationTemplate.d.ts +12 -0
  60. package/build/components/KYCElements/PersonalInformationTemplate.d.ts.map +1 -0
  61. package/build/components/KYCElements/PersonalInformationTemplate.js +120 -0
  62. package/build/components/KYCElements/PersonalInformationTemplate.js.map +1 -0
  63. package/build/components/KYCElements/PhoneVerificationTemplate.d.ts +12 -0
  64. package/build/components/KYCElements/PhoneVerificationTemplate.d.ts.map +1 -0
  65. package/build/components/KYCElements/PhoneVerificationTemplate.js +185 -0
  66. package/build/components/KYCElements/PhoneVerificationTemplate.js.map +1 -0
  67. package/build/components/KYCElements/SelfieCapture.d.ts.map +1 -1
  68. package/build/components/KYCElements/SelfieCapture.js +4 -3
  69. package/build/components/KYCElements/SelfieCapture.js.map +1 -1
  70. package/build/components/KYCElements/SelfieCaptureTemplate.d.ts.map +1 -1
  71. package/build/components/KYCElements/SelfieCaptureTemplate.js +189 -42
  72. package/build/components/KYCElements/SelfieCaptureTemplate.js.map +1 -1
  73. package/build/components/KYCElements/WelcomeTemplate.d.ts +12 -0
  74. package/build/components/KYCElements/WelcomeTemplate.d.ts.map +1 -0
  75. package/build/components/KYCElements/WelcomeTemplate.js +243 -0
  76. package/build/components/KYCElements/WelcomeTemplate.js.map +1 -0
  77. package/build/components/TemplateKYCExample.d.ts +8 -2
  78. package/build/components/TemplateKYCExample.d.ts.map +1 -1
  79. package/build/components/TemplateKYCExample.js +10 -97
  80. package/build/components/TemplateKYCExample.js.map +1 -1
  81. package/build/components/TemplateKYCFlowRefactored.d.ts +6 -1
  82. package/build/components/TemplateKYCFlowRefactored.d.ts.map +1 -1
  83. package/build/components/TemplateKYCFlowRefactored.js +108 -11
  84. package/build/components/TemplateKYCFlowRefactored.js.map +1 -1
  85. package/build/components/example/DynamicTemplateExample.d.ts +10 -0
  86. package/build/components/example/DynamicTemplateExample.d.ts.map +1 -0
  87. package/build/components/example/DynamicTemplateExample.js +241 -0
  88. package/build/components/example/DynamicTemplateExample.js.map +1 -0
  89. package/build/config/KYCConfig.d.ts +14 -0
  90. package/build/config/KYCConfig.d.ts.map +1 -0
  91. package/build/config/KYCConfig.js +26 -0
  92. package/build/config/KYCConfig.js.map +1 -0
  93. package/build/config/allowedDomains.d.ts +30 -0
  94. package/build/config/allowedDomains.d.ts.map +1 -0
  95. package/build/config/allowedDomains.js +112 -0
  96. package/build/config/allowedDomains.js.map +1 -0
  97. package/build/hooks/useOrientationVideo.d.ts +2 -1
  98. package/build/hooks/useOrientationVideo.d.ts.map +1 -1
  99. package/build/hooks/useOrientationVideo.js +3 -3
  100. package/build/hooks/useOrientationVideo.js.map +1 -1
  101. package/build/hooks/useTemplateKYCFlow.d.ts +6 -1
  102. package/build/hooks/useTemplateKYCFlow.d.ts.map +1 -1
  103. package/build/hooks/useTemplateKYCFlow.js +317 -34
  104. package/build/hooks/useTemplateKYCFlow.js.map +1 -1
  105. package/build/hooks/useTemplateLoader.d.ts +14 -0
  106. package/build/hooks/useTemplateLoader.d.ts.map +1 -0
  107. package/build/hooks/useTemplateLoader.js +85 -0
  108. package/build/hooks/useTemplateLoader.js.map +1 -0
  109. package/build/i18n/en/index.d.ts +49 -0
  110. package/build/i18n/en/index.d.ts.map +1 -1
  111. package/build/i18n/en/index.js +50 -1
  112. package/build/i18n/en/index.js.map +1 -1
  113. package/build/i18n/fr/index.d.ts +35 -0
  114. package/build/i18n/fr/index.d.ts.map +1 -1
  115. package/build/i18n/fr/index.js +36 -1
  116. package/build/i18n/fr/index.js.map +1 -1
  117. package/build/index.d.ts +6 -0
  118. package/build/index.d.ts.map +1 -1
  119. package/build/index.js +10 -0
  120. package/build/index.js.map +1 -1
  121. package/build/modules/api/CardAuthentification.d.ts +24 -3
  122. package/build/modules/api/CardAuthentification.d.ts.map +1 -1
  123. package/build/modules/api/CardAuthentification.js +69 -10
  124. package/build/modules/api/CardAuthentification.js.map +1 -1
  125. package/build/modules/api/KYCService.d.ts +7 -7
  126. package/build/modules/api/KYCService.d.ts.map +1 -1
  127. package/build/modules/api/KYCService.js +108 -39
  128. package/build/modules/api/KYCService.js.map +1 -1
  129. package/build/modules/api/SelfieVerification.d.ts +3 -1
  130. package/build/modules/api/SelfieVerification.d.ts.map +1 -1
  131. package/build/modules/api/SelfieVerification.js +17 -1
  132. package/build/modules/api/SelfieVerification.js.map +1 -1
  133. package/build/modules/api/TemplateService.d.ts +44 -0
  134. package/build/modules/api/TemplateService.d.ts.map +1 -0
  135. package/build/modules/api/TemplateService.js +145 -0
  136. package/build/modules/api/TemplateService.js.map +1 -0
  137. package/build/types/KYC.types.d.ts +265 -4
  138. package/build/types/KYC.types.d.ts.map +1 -1
  139. package/build/types/KYC.types.js +15 -0
  140. package/build/types/KYC.types.js.map +1 -1
  141. package/build/types/env.types.d.ts +13 -0
  142. package/build/types/env.types.d.ts.map +1 -0
  143. package/build/types/env.types.js +2 -0
  144. package/build/types/env.types.js.map +1 -0
  145. package/build/utils/cropByObb.d.ts +1 -0
  146. package/build/utils/cropByObb.d.ts.map +1 -1
  147. package/build/utils/cropByObb.js +70 -0
  148. package/build/utils/cropByObb.js.map +1 -1
  149. package/build/utils/deviceDetection.d.ts +6 -0
  150. package/build/utils/deviceDetection.d.ts.map +1 -0
  151. package/build/utils/deviceDetection.js +12 -0
  152. package/build/utils/deviceDetection.js.map +1 -0
  153. package/build/utils/platformAlert.d.ts +20 -0
  154. package/build/utils/platformAlert.d.ts.map +1 -0
  155. package/build/utils/platformAlert.js +67 -0
  156. package/build/utils/platformAlert.js.map +1 -0
  157. package/build/utils/template-transformer.d.ts +10 -0
  158. package/build/utils/template-transformer.d.ts.map +1 -0
  159. package/build/utils/template-transformer.js +365 -0
  160. package/build/utils/template-transformer.js.map +1 -0
  161. package/build/web/WebKYCEntry.d.ts.map +1 -1
  162. package/build/web/WebKYCEntry.js +158 -32
  163. package/build/web/WebKYCEntry.js.map +1 -1
  164. package/package.json +1 -1
  165. package/plugin/build/withVisionCamera.js +3 -4
  166. package/plugin/src/withVisionCamera.js +3 -4
  167. package/plugin/src/withVisionCamera.ts +3 -4
  168. package/src/components/EnhancedCameraView.tsx +31 -2
  169. package/src/components/EnhancedCameraView.web.tsx +24 -0
  170. package/src/components/KYCElements/AdditionalDocumentsTemplate.tsx +346 -0
  171. package/src/components/KYCElements/CameraCapture.tsx +4 -3
  172. package/src/components/KYCElements/CountrySelectionTemplate.tsx +410 -113
  173. package/src/components/KYCElements/EmailVerificationTemplate.tsx +264 -0
  174. package/src/components/KYCElements/FileUpload.tsx +5 -4
  175. package/src/components/KYCElements/FileUploadTemplate.tsx +5 -4
  176. package/src/components/KYCElements/IDCardCapture.tsx +397 -254
  177. package/src/components/KYCElements/LocationCaptureTemplate.tsx +95 -44
  178. package/src/components/KYCElements/OrientationVideoCapture.tsx +6 -3
  179. package/src/components/KYCElements/OrientationVideoCaptureEnhanced.tsx +6 -3
  180. package/src/components/KYCElements/OrientationVideoCaptureFinal.tsx +6 -3
  181. package/src/components/KYCElements/PersonalInformationTemplate.tsx +158 -0
  182. package/src/components/KYCElements/PhoneVerificationTemplate.tsx +253 -0
  183. package/src/components/KYCElements/SelfieCapture.tsx +4 -3
  184. package/src/components/KYCElements/SelfieCaptureTemplate.tsx +201 -44
  185. package/src/components/KYCElements/WelcomeTemplate.tsx +289 -0
  186. package/src/components/TemplateKYCExample.tsx +37 -108
  187. package/src/components/TemplateKYCFlowRefactored.tsx +148 -12
  188. package/src/components/example/DynamicTemplateExample.tsx +289 -0
  189. package/src/config/KYCConfig.ts +34 -0
  190. package/src/config/allowedDomains.ts +133 -0
  191. package/src/hooks/useOrientationVideo.ts +5 -4
  192. package/src/hooks/useTemplateKYCFlow.tsx +347 -32
  193. package/src/hooks/useTemplateLoader.ts +102 -0
  194. package/src/i18n/en/index.ts +53 -2
  195. package/src/i18n/fr/index.ts +37 -1
  196. package/src/index.ts +14 -0
  197. package/src/modules/api/CardAuthentification.ts +76 -11
  198. package/src/modules/api/KYCService.ts +129 -45
  199. package/src/modules/api/SelfieVerification.ts +25 -3
  200. package/src/modules/api/TemplateService.ts +167 -0
  201. package/src/types/KYC.types.ts +331 -3
  202. package/src/types/env.types.ts +13 -0
  203. package/src/utils/cropByObb.ts +83 -3
  204. package/src/utils/deviceDetection.ts +11 -0
  205. package/src/utils/platformAlert.ts +86 -0
  206. package/src/utils/template-transformer.ts +445 -0
  207. package/src/web/WebKYCEntry.tsx +199 -50
@@ -0,0 +1,253 @@
1
+ import React, { useState } from 'react';
2
+ import { View, Text, StyleSheet, TextInput, TouchableOpacity, Alert } from 'react-native';
3
+ import { TemplateComponent, LocalizedText } from '../../types/KYC.types';
4
+ import { useTemplateKYCFlowContext } from '../../hooks/useTemplateKYCFlow';
5
+ import { useI18n } from '../../hooks/useI18n';
6
+ import { Button } from '../ui/Button';
7
+
8
+ interface PhoneVerificationTemplateProps {
9
+ component: TemplateComponent;
10
+ value?: any;
11
+ onValueChange: (data: any) => void;
12
+ error?: string;
13
+ language?: string;
14
+ }
15
+
16
+ type VerificationStep = 'phone' | 'otp';
17
+
18
+ export const PhoneVerificationTemplate: React.FC<PhoneVerificationTemplateProps> = ({
19
+ component,
20
+ value,
21
+ onValueChange,
22
+ error: propError,
23
+ }) => {
24
+ const { actions, getLocalizedText } = useTemplateKYCFlowContext();
25
+ const { t } = useI18n();
26
+ // const config = component.config as PhoneVerificationConfig;
27
+
28
+ // State
29
+ const [step, setStep] = useState<VerificationStep>('phone');
30
+ const [phone, setPhone] = useState('');
31
+ const [otp, setOtp] = useState('');
32
+ const [localError, setLocalError] = useState<string | null>(null);
33
+ const [isSimulating, setIsSimulating] = useState(false);
34
+
35
+ const title = getLocalizedText(component.labels as LocalizedText);
36
+ const instructions = getLocalizedText(component.instructions as LocalizedText);
37
+
38
+ // Determine button text based on step
39
+ const verifyButtonText = getLocalizedText((component.ui as any).buttonText) || t('common.verify') || 'Verify';
40
+ const sendButtonText = t('common.sendCode') || 'Send Verification Code';
41
+ const buttonText = step === 'phone' ? sendButtonText : verifyButtonText;
42
+
43
+ const handleSendCode = () => {
44
+ if (!phone || phone.length < 5) {
45
+ setLocalError(t('errors.invalidPhone') || 'Please enter a valid phone number');
46
+ return;
47
+ }
48
+
49
+ setLocalError(null);
50
+ setIsSimulating(true);
51
+
52
+ // Simulate API call to send code
53
+ setTimeout(() => {
54
+ setIsSimulating(false);
55
+ setStep('otp');
56
+ }, 1500);
57
+ };
58
+
59
+ const handleVerifyCode = () => {
60
+ if (!otp || otp.length < 4) {
61
+ setLocalError(t('errors.invalidCode') || 'Please enter the 6-digit code');
62
+ return;
63
+ }
64
+
65
+ setLocalError(null);
66
+ setIsSimulating(true);
67
+
68
+ // Simulate verification API
69
+ setTimeout(() => {
70
+ setIsSimulating(false);
71
+
72
+ // Mock validation logic
73
+ if (otp === '123456') {
74
+ onValueChange({ phone, otp, verified: true });
75
+ actions.nextComponent();
76
+ } else {
77
+ setLocalError(t('errors.wrongCode') || 'Invalid verification code. Try 123456');
78
+ }
79
+ }, 1500);
80
+ };
81
+
82
+ const onChangePhone = (text: string) => {
83
+ setPhone(text);
84
+ if (localError) setLocalError(null);
85
+ };
86
+
87
+ const onChangeOtp = (text: string) => {
88
+ setOtp(text);
89
+ if (localError) setLocalError(null);
90
+ };
91
+
92
+ const handleBackToPhone = () => {
93
+ setStep('phone');
94
+ setOtp('');
95
+ setLocalError(null);
96
+ };
97
+
98
+ return (
99
+ <View style={styles.container}>
100
+ <Text style={styles.title}>{title}</Text>
101
+ <Text style={styles.instructions}>
102
+ {step === 'phone' ? instructions : (t('kyc.enterCodeSent') || `Please enter the code sent to ${phone}`)}
103
+ </Text>
104
+
105
+ <View style={styles.contentContainer}>
106
+ {step === 'phone' ? (
107
+ <View style={styles.inputContainer}>
108
+ <Text style={styles.label}>{t('common.phone') || 'Phone Number'}</Text>
109
+ <TextInput
110
+ style={styles.input}
111
+ placeholder="+1234567890"
112
+ value={phone}
113
+ onChangeText={onChangePhone}
114
+ keyboardType="phone-pad"
115
+ autoComplete="tel"
116
+ editable={!isSimulating}
117
+ />
118
+ </View>
119
+ ) : (
120
+ <View style={styles.inputContainer}>
121
+ <Text style={styles.label}>{t('common.verificationCode') || 'Verification Code'}</Text>
122
+ <TextInput
123
+ style={styles.input}
124
+ placeholder="123456"
125
+ value={otp}
126
+ onChangeText={onChangeOtp}
127
+ keyboardType="number-pad"
128
+ maxLength={6}
129
+ editable={!isSimulating}
130
+ />
131
+ <TouchableOpacity onPress={handleBackToPhone} style={styles.changeLink}>
132
+ <Text style={styles.changeText}>{t('common.back') || 'Change number'}</Text>
133
+ </TouchableOpacity>
134
+ </View>
135
+ )}
136
+
137
+ {(localError || propError) && (
138
+ <Text style={styles.errorText}>{localError || propError}</Text>
139
+ )}
140
+
141
+ <Button
142
+ title={isSimulating ? (t('common.processing') || 'Processing...') : buttonText}
143
+ onPress={step === 'phone' ? handleSendCode : handleVerifyCode}
144
+ style={styles.button}
145
+ disabled={
146
+ isSimulating ||
147
+ (step === 'phone' ? !phone : !otp)
148
+ }
149
+ />
150
+
151
+ {step === 'otp' && (
152
+ <TouchableOpacity
153
+ onPress={() => {
154
+ // Resend logic
155
+ Alert.alert(
156
+ t('common.codeResent') || 'Code Resent',
157
+ t('common.codeResentMessage', { email: phone }) || 'Code resent to ' + phone
158
+ );
159
+ }}
160
+ style={styles.resendButton}
161
+ disabled={isSimulating}
162
+ >
163
+ <Text style={styles.resendText}>{t('common.resendCode') || 'Resend Code'}</Text>
164
+ </TouchableOpacity>
165
+ )}
166
+ </View>
167
+ </View>
168
+ );
169
+ };
170
+
171
+ const styles = StyleSheet.create({
172
+ container: {
173
+ padding: 24,
174
+ backgroundColor: 'white',
175
+ borderRadius: 16,
176
+ margin: 16,
177
+ shadowColor: '#000',
178
+ shadowOffset: { width: 0, height: 4 },
179
+ shadowOpacity: 0.1,
180
+ shadowRadius: 12,
181
+ elevation: 5,
182
+ width: '95%',
183
+ },
184
+ title: {
185
+ fontSize: 24,
186
+ fontWeight: '700',
187
+ marginBottom: 8,
188
+ color: '#1a1a1a',
189
+ textAlign: 'center',
190
+ },
191
+ instructions: {
192
+ fontSize: 16,
193
+ color: '#666',
194
+ marginBottom: 32,
195
+ lineHeight: 24,
196
+ textAlign: 'center',
197
+ },
198
+ contentContainer: {
199
+ // width: '100%',
200
+ },
201
+ inputContainer: {
202
+ marginBottom: 24,
203
+ },
204
+ label: {
205
+ fontSize: 14,
206
+ fontWeight: '600',
207
+ color: '#333',
208
+ marginBottom: 8,
209
+ marginLeft: 4,
210
+ },
211
+ input: {
212
+ borderWidth: 1,
213
+ borderColor: '#e0e0e0',
214
+ padding: 16,
215
+ borderRadius: 12,
216
+ fontSize: 16,
217
+ backgroundColor: '#f8f9fa',
218
+ color: '#333',
219
+ },
220
+ errorText: {
221
+ color: '#dc2626',
222
+ marginBottom: 16,
223
+ fontSize: 14,
224
+ textAlign: 'center',
225
+ backgroundColor: '#fee2e2',
226
+ padding: 8,
227
+ borderRadius: 8,
228
+ overflow: 'hidden',
229
+ },
230
+ button: {
231
+ height: 50,
232
+ borderRadius: 12,
233
+ width: "100%"
234
+ },
235
+ changeLink: {
236
+ alignSelf: 'flex-end',
237
+ marginTop: 8,
238
+ },
239
+ changeText: {
240
+ color: '#2DBD60',
241
+ fontSize: 14,
242
+ fontWeight: '500',
243
+ },
244
+ resendButton: {
245
+ marginTop: 16,
246
+ alignItems: 'center',
247
+ },
248
+ resendText: {
249
+ color: '#666',
250
+ fontSize: 14,
251
+ textDecorationLine: 'underline',
252
+ },
253
+ });
@@ -1,5 +1,6 @@
1
1
  import React, { useState } from 'react';
2
- import { View, Text, TouchableOpacity, StyleSheet, Image, Alert } from 'react-native';
2
+ import { View, Text, TouchableOpacity, StyleSheet, Image } from 'react-native';
3
+ import { showAlert } from '../../utils/platformAlert';
3
4
  import { EnhancedCameraView } from '../EnhancedCameraView';
4
5
 
5
6
  import { KYCElement } from '../../types/KYC.types';
@@ -39,12 +40,12 @@ export const SelfieCapture: React.FC<SelfieCaptureProps> = ({
39
40
  onValueChange(result.path);
40
41
  setShowCamera(false);
41
42
  } else {
42
- Alert.alert('Erreur', result.error || 'Impossible de prendre le selfie');
43
+ showAlert('Erreur', result.error || 'Impossible de prendre le selfie');
43
44
  }
44
45
  };
45
46
 
46
47
  const handleError = (event: { message: string }) => {
47
- Alert.alert('Erreur', event.message);
48
+ showAlert('Erreur', event.message);
48
49
  setShowCamera(false);
49
50
  };
50
51
 
@@ -1,6 +1,7 @@
1
1
  import React, { useEffect, useMemo, useState } from 'react';
2
- import { View, Text, StyleSheet, Alert, ScrollView } from 'react-native';
3
- import { TemplateComponent, SelfieConfig, LocalizedText, ISilentCaptureResult, OrientationType, GovernmentDocumentType } from '../../types/KYC.types';
2
+ import { View, Text, StyleSheet, ScrollView, Image, TouchableOpacity } from 'react-native';
3
+ import { showAlert } from '../../utils/platformAlert';
4
+ import { TemplateComponent, LocalizedText, ISilentCaptureResult, OrientationType, GovernmentDocumentType } from '../../types/KYC.types';
4
5
  import { EnhancedCameraView } from '../EnhancedCameraView';
5
6
  import { Button } from '../ui/Button';
6
7
  import { useTemplateKYCFlowContext } from '../../hooks/useTemplateKYCFlow';
@@ -30,14 +31,15 @@ export const SelfieCaptureTemplate: React.FC<SelfieCaptureTemplateProps> = ({
30
31
  language,
31
32
  }) => {
32
33
  const { t } = useI18n();
33
- const config = component.config as SelfieConfig;
34
- const orientations: OrientationType[] = (config.orientations || ['center']) as OrientationType[];
35
- const { actions, state, } = useTemplateKYCFlowContext();
34
+ // const config = component.config as SelfieConfig;
35
+ const orientations: OrientationType[] = (['center','left','right']) as OrientationType[];
36
+ const { actions, state, env } = useTemplateKYCFlowContext();
36
37
 
37
38
  const [silentCaptureResult, setSilentCaptureResult] = useState<ISilentCaptureResult>({ success: false, isAnalyzing: false });
38
39
 
39
-
40
40
  const [showCamera, setShowCamera] = useState(false);
41
+ const [showPreview, setShowPreview] = useState(false);
42
+ const [previewImagePath, setPreviewImagePath] = useState<string | null>(null);
41
43
  const [currentOrientation, setCurrentOrientation] = useState<OrientationType>(orientations[0]);
42
44
  const [capturedImages, setCapturedImages] = useState<Record<string, IImagePayload>>(value || {});
43
45
 
@@ -78,7 +80,7 @@ export const SelfieCaptureTemplate: React.FC<SelfieCaptureTemplateProps> = ({
78
80
  case 'left':
79
81
  return state.currentLanguage === "en" ? "Left Profil Selfie" : "Selfie profil gauche";
80
82
  case 'right':
81
- return state.currentLanguage === "en" ? "Rigth Profil Selfiee" : "Selfie profil droit";
83
+ return state.currentLanguage === "en" ? "Right Profile Selfie" : "Selfie profil droit";
82
84
  default:
83
85
  return getLocalizedText(component.labels as LocalizedText);
84
86
  }
@@ -150,10 +152,13 @@ export const SelfieCaptureTemplate: React.FC<SelfieCaptureTemplateProps> = ({
150
152
  }
151
153
  if (result.success && result.path) {
152
154
  setSilentCaptureResult((prev) => ({ ...prev, isAnalyzing: true, success: false, error: '' }));
153
- selfieVerification(result.path).then((response) => {
155
+ selfieVerification(result.path, env).then((response) => {
154
156
  if (response.length > 0) {
155
157
  const res = response[0];
156
- if (res?.orientation_direction === getOrientationOpposite(currentOrientation) && res?.capture) {
158
+ // In SANDBOX mode, always accept the result regardless of orientation
159
+ if (env === 'SANDBOX' && res?.capture) {
160
+ setSilentCaptureResult((prev) => ({ ...prev, isAnalyzing: false, success: true, error: '', path: result.path }));
161
+ } else if (res?.orientation_direction === getOrientationOpposite(currentOrientation) && res?.capture) {
157
162
  setSilentCaptureResult((prev) => ({ ...prev, isAnalyzing: false, success: true, error: '', path: result.path }));
158
163
  } else {
159
164
  setSilentCaptureResult((prev) => ({ ...prev, isAnalyzing: false, success: false, error: 'Le selfie n\'est pas correct' }));
@@ -162,39 +167,80 @@ export const SelfieCaptureTemplate: React.FC<SelfieCaptureTemplateProps> = ({
162
167
  setSilentCaptureResult((prev) => ({ ...prev, isAnalyzing: false, success: false, error: 'Le selfie n\'est pas correct' }));
163
168
  }
164
169
  }).catch((e: any) => {
165
-
166
170
  setSilentCaptureResult((prev) => ({ ...prev, isAnalyzing: false, success: false, error: e?.message || 'Erreur de vérification du selfie' }));
167
171
  });
168
172
  }
169
173
  }
170
174
 
175
+ // Capture manuelle - affiche le preview
171
176
  const handleCapture = async (result: { success: boolean; path?: string; error?: string }) => {
172
- console.log("handleCapturessss", silentCaptureResult);
177
+ console.log("handleCapture called", result, silentCaptureResult);
173
178
 
179
+ // Priorité 1: Si la capture silencieuse a réussi, utiliser ce path
174
180
  if (silentCaptureResult.success && silentCaptureResult.path) {
175
- const base64 = await pathToBase64(silentCaptureResult.path);
176
- // Keep backward-compatible structure for UI validation and add *_base64 fields for backend payload
181
+ setPreviewImagePath(silentCaptureResult.path);
182
+ setShowCamera(false);
183
+ setShowPreview(true);
184
+ return;
185
+ }
186
+
187
+ // Priorité 2: Capture manuelle normale
188
+ if (result.success && result.path) {
189
+ setPreviewImagePath(result.path);
190
+ setShowCamera(false);
191
+ setShowPreview(true);
192
+ }
193
+ };
194
+
195
+ // Confirmer la capture depuis le preview
196
+ const handleConfirmCapture = async () => {
197
+ if (!previewImagePath) return;
198
+
199
+ try {
200
+ const base64 = await pathToBase64(previewImagePath);
177
201
  const newImages: Record<string, any> = {
178
202
  ...capturedImages,
179
- [currentOrientation]: { dir: silentCaptureResult.path, file: base64 } as IImagePayload,
203
+ [currentOrientation]: { dir: previewImagePath, file: base64 } as IImagePayload,
180
204
  };
181
205
  setCapturedImages(newImages);
182
206
  onValueChange(newImages);
183
- setShowCamera(false);
184
-
207
+ setShowPreview(false);
208
+ setPreviewImagePath(null);
209
+ setSilentCaptureResult({ success: false, isAnalyzing: false });
185
210
 
186
211
  // Passer à l'orientation suivante si disponible
187
212
  const currentIndex = orientations.indexOf(currentOrientation as OrientationType);
188
213
  if (currentIndex < orientations.length - 1) {
189
- setCurrentOrientation(orientations[currentIndex + 1]);
214
+ const nextOrientation = orientations[currentIndex + 1];
215
+ setCurrentOrientation(nextOrientation);
216
+ // Rouvrir automatiquement la caméra pour la prochaine orientation
217
+ setShowCamera(true);
190
218
  }
219
+ // Si toutes les orientations sont complétées, on reste sur la vue principale
220
+ } catch (e) {
221
+ console.error("Error confirming capture", e);
222
+ showAlert('Erreur', 'Erreur lors de la sauvegarde de l\'image');
191
223
  }
192
224
  };
225
+
226
+ // Reprendre la photo depuis le preview
227
+ const handleRetakeFromPreview = () => {
228
+ setShowPreview(false);
229
+ setPreviewImagePath(null);
230
+ setSilentCaptureResult({ success: false, isAnalyzing: false });
231
+ setShowCamera(true);
232
+ };
193
233
  const idCardData = useMemo(() => {
194
234
  const idCardID = Object.keys(state.componentData).find((c: string) => c === "1");
195
235
  if (idCardID) {
196
236
  const _idCardData = state.componentData[idCardID];
197
- return _idCardData as unknown as { country: string; documentType: GovernmentDocumentType };
237
+ const documentType = _idCardData?.documentType;
238
+ // Map national_id to identity_card for selfie capture
239
+ const mappedDocumentType = documentType === 'national_id' ? 'identity_card' : documentType;
240
+ return {
241
+ country: _idCardData?.country,
242
+ documentType: mappedDocumentType as GovernmentDocumentType
243
+ };
198
244
  }
199
245
  return null;
200
246
  }, [state.componentData]);
@@ -202,7 +248,7 @@ export const SelfieCaptureTemplate: React.FC<SelfieCaptureTemplateProps> = ({
202
248
 
203
249
 
204
250
  const handleError = (event: { message: string }) => {
205
- Alert.alert('Erreur', event.message);
251
+ showAlert('Erreur', event.message);
206
252
  setShowCamera(false);
207
253
  };
208
254
 
@@ -216,7 +262,60 @@ export const SelfieCaptureTemplate: React.FC<SelfieCaptureTemplateProps> = ({
216
262
  };
217
263
  console.log("Current Orientation", currentOrientation);
218
264
 
265
+ // Vue Preview
266
+ if (showPreview && previewImagePath) {
267
+ return (
268
+ <View style={styles.containerCamera}>
269
+ <View style={styles.previewContainer}>
270
+ <View style={styles.previewHeader}>
271
+ <TouchableOpacity onPress={handleRetakeFromPreview} style={styles.backButton}>
272
+ <Text style={styles.backButtonText}>←</Text>
273
+ </TouchableOpacity>
274
+ <Text style={styles.previewTitle}>
275
+ {state.currentLanguage === "en" ? "Preview" : "Aperçu"}
276
+ </Text>
277
+ <View style={{ width: 40 }} />
278
+ </View>
279
+
280
+ <View style={styles.previewImageContainer}>
281
+ <Image
282
+ source={{ uri: previewImagePath }}
283
+ style={styles.previewImage}
284
+ resizeMode="contain"
285
+ />
286
+ </View>
219
287
 
288
+ <Text style={styles.previewInstructions}>
289
+ {state.currentLanguage === "en"
290
+ ? "Is your face clearly visible?"
291
+ : "Votre visage est-il clairement visible ?"}
292
+ </Text>
293
+
294
+ <View style={styles.previewButtonsContainer}>
295
+ <TouchableOpacity
296
+ style={styles.retakeButton}
297
+ onPress={handleRetakeFromPreview}
298
+ >
299
+ <Text style={styles.retakeButtonText}>
300
+ {state.currentLanguage === "en" ? "Retake" : "Reprendre"}
301
+ </Text>
302
+ </TouchableOpacity>
303
+
304
+ <TouchableOpacity
305
+ style={styles.confirmButton}
306
+ onPress={handleConfirmCapture}
307
+ >
308
+ <Text style={styles.confirmButtonText}>
309
+ {state.currentLanguage === "en" ? "Use Photo" : "Utiliser"}
310
+ </Text>
311
+ </TouchableOpacity>
312
+ </View>
313
+ </View>
314
+ </View>
315
+ );
316
+ }
317
+
318
+ // Vue Camera
220
319
  if (showCamera) {
221
320
  return (
222
321
  <View style={styles.containerCamera}>
@@ -322,18 +421,13 @@ export const SelfieCaptureTemplate: React.FC<SelfieCaptureTemplateProps> = ({
322
421
  <View style={{ height: 10 }} />
323
422
  {isAllOrientationsCompleted() ? <>
324
423
  <Button title={t('common.continue')} fullWidth style={{ paddingVertical: 20, paddingTop: 12 }} onPress={async () => {
325
- if (idCardData?.country && idCardData?.documentType) {
326
- const value = {
327
- ...capturedImages,
328
- country: idCardData?.country || '',
329
- documentType: idCardData?.documentType as GovernmentDocumentType,
330
- }
331
- console.log("value", JSON.stringify(truncateFields(value), null, 2));
332
- onValueChange(value);
333
- } else {
334
- Alert.alert('Erreur', 'Veuillez sélectionner un pays et un type de document');
335
- return;
424
+ const value = {
425
+ ...capturedImages,
426
+ ...(idCardData?.country ? { country: idCardData.country } : {}),
427
+ ...(idCardData?.documentType ? { documentType: idCardData.documentType } : {}),
336
428
  }
429
+ console.log("value", JSON.stringify(truncateFields(value), null, 2));
430
+ onValueChange(value);
337
431
  actions.nextComponent();
338
432
  }} />
339
433
  </> : (
@@ -370,23 +464,17 @@ const styles = StyleSheet.create({
370
464
  borderRadius: 20,
371
465
  paddingVertical: 20,
372
466
  paddingHorizontal: 16,
373
- // shadow
374
467
  shadowColor: '#000',
375
468
  shadowOffset: { width: 0, height: 2 },
376
469
  shadowOpacity: 0.35,
377
470
  shadowRadius: 4.84,
378
471
  elevation: 10,
379
-
380
-
381
- // padding: 16,
382
472
  },
383
473
  containerCamera: {
384
474
  flex: 1,
385
- // maxWidth: 760,
386
475
  width: '100%',
387
476
  height: '100%',
388
- // borderRadius: 12,
389
- backgroundColor: '#f5f5f5',
477
+ backgroundColor: '#000',
390
478
  },
391
479
  title: {
392
480
  fontSize: 24,
@@ -404,7 +492,6 @@ const styles = StyleSheet.create({
404
492
  },
405
493
  camera: {
406
494
  flex: 1,
407
- // borderRadius: 12,
408
495
  overflow: 'hidden',
409
496
  },
410
497
  attemptsText: {
@@ -414,7 +501,6 @@ const styles = StyleSheet.create({
414
501
  marginBottom: 10,
415
502
  },
416
503
  orientationsContainer: {
417
- // flex: 1,
418
504
  },
419
505
  orientationContainer: {
420
506
  backgroundColor: 'white',
@@ -434,7 +520,6 @@ const styles = StyleSheet.create({
434
520
  fontSize: 18,
435
521
  fontWeight: '600',
436
522
  color: '#333',
437
- // marginBottom: 12,
438
523
  },
439
524
  capturedImageContainer: {
440
525
  alignItems: 'center',
@@ -464,14 +549,86 @@ const styles = StyleSheet.create({
464
549
  fontWeight: '600',
465
550
  color: 'white',
466
551
  },
552
+ // Preview styles
553
+ previewContainer: {
554
+ flex: 1,
555
+ backgroundColor: '#000',
556
+ justifyContent: 'space-between',
557
+ },
558
+ previewHeader: {
559
+ flexDirection: 'row',
560
+ alignItems: 'center',
561
+ justifyContent: 'space-between',
562
+ paddingHorizontal: 16,
563
+ paddingTop: 50,
564
+ paddingBottom: 16,
565
+ },
566
+ backButton: {
567
+ width: 40,
568
+ height: 40,
569
+ borderRadius: 20,
570
+ backgroundColor: 'rgba(255,255,255,0.2)',
571
+ justifyContent: 'center',
572
+ alignItems: 'center',
573
+ },
574
+ backButtonText: {
575
+ color: 'white',
576
+ fontSize: 24,
577
+ fontWeight: 'bold',
578
+ },
579
+ previewTitle: {
580
+ color: 'white',
581
+ fontSize: 18,
582
+ fontWeight: '600',
583
+ },
584
+ previewImageContainer: {
585
+ flex: 1,
586
+ justifyContent: 'center',
587
+ alignItems: 'center',
588
+ paddingHorizontal: 20,
589
+ },
590
+ previewImage: {
591
+ width: '100%',
592
+ height: '80%',
593
+ borderRadius: 16,
594
+ },
595
+ previewInstructions: {
596
+ color: 'white',
597
+ fontSize: 16,
598
+ textAlign: 'center',
599
+ paddingHorizontal: 20,
600
+ marginBottom: 20,
601
+ },
602
+ previewButtonsContainer: {
603
+ flexDirection: 'row',
604
+ justifyContent: 'space-between',
605
+ paddingHorizontal: 20,
606
+ paddingBottom: 40,
607
+ gap: 16,
608
+ },
467
609
  retakeButton: {
468
- backgroundColor: '#FF6B6B',
469
- paddingVertical: 12,
610
+ flex: 1,
611
+ backgroundColor: 'rgba(255,255,255,0.2)',
612
+ paddingVertical: 16,
470
613
  paddingHorizontal: 20,
471
- borderRadius: 8,
614
+ borderRadius: 12,
615
+ alignItems: 'center',
472
616
  },
473
617
  retakeButtonText: {
474
- fontSize: 14,
618
+ fontSize: 16,
619
+ fontWeight: '600',
620
+ color: 'white',
621
+ },
622
+ confirmButton: {
623
+ flex: 1,
624
+ backgroundColor: '#2DBD60',
625
+ paddingVertical: 16,
626
+ paddingHorizontal: 20,
627
+ borderRadius: 12,
628
+ alignItems: 'center',
629
+ },
630
+ confirmButtonText: {
631
+ fontSize: 16,
475
632
  fontWeight: '600',
476
633
  color: 'white',
477
634
  },