@transfergratis/react-native-sdk 0.1.4 → 0.1.5

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 (199) hide show
  1. package/build/api/axios.d.ts +30 -0
  2. package/build/api/axios.d.ts.map +1 -0
  3. package/build/api/axios.js +92 -0
  4. package/build/api/axios.js.map +1 -0
  5. package/build/components/EnhancedCameraView.d.ts +1 -41
  6. package/build/components/EnhancedCameraView.d.ts.map +1 -1
  7. package/build/components/EnhancedCameraView.js +75 -34
  8. package/build/components/EnhancedCameraView.js.map +1 -1
  9. package/build/components/EnhancedCameraView.web.d.ts +1 -41
  10. package/build/components/EnhancedCameraView.web.d.ts.map +1 -1
  11. package/build/components/EnhancedCameraView.web.js +28 -4
  12. package/build/components/EnhancedCameraView.web.js.map +1 -1
  13. package/build/components/KYCElements/CountrySelectionTemplate.d.ts +2 -2
  14. package/build/components/KYCElements/CountrySelectionTemplate.d.ts.map +1 -1
  15. package/build/components/KYCElements/CountrySelectionTemplate.js +71 -114
  16. package/build/components/KYCElements/CountrySelectionTemplate.js.map +1 -1
  17. package/build/components/KYCElements/FileUploadTemplate.d.ts.map +1 -1
  18. package/build/components/KYCElements/FileUploadTemplate.js +7 -3
  19. package/build/components/KYCElements/FileUploadTemplate.js.map +1 -1
  20. package/build/components/KYCElements/IDCardCapture.d.ts +7 -2
  21. package/build/components/KYCElements/IDCardCapture.d.ts.map +1 -1
  22. package/build/components/KYCElements/IDCardCapture.js +253 -104
  23. package/build/components/KYCElements/IDCardCapture.js.map +1 -1
  24. package/build/components/KYCElements/InitializationStep.d.ts +5 -0
  25. package/build/components/KYCElements/InitializationStep.d.ts.map +1 -0
  26. package/build/components/KYCElements/InitializationStep.js +41 -0
  27. package/build/components/KYCElements/InitializationStep.js.map +1 -0
  28. package/build/components/KYCElements/LocationCaptureTemplate.d.ts.map +1 -1
  29. package/build/components/KYCElements/LocationCaptureTemplate.js +15 -13
  30. package/build/components/KYCElements/LocationCaptureTemplate.js.map +1 -1
  31. package/build/components/KYCElements/OrientationVideoCapture.d.ts +2 -2
  32. package/build/components/KYCElements/OrientationVideoCapture.d.ts.map +1 -1
  33. package/build/components/KYCElements/OrientationVideoCapture.js.map +1 -1
  34. package/build/components/KYCElements/OrientationVideoCaptureEnhanced.d.ts +2 -2
  35. package/build/components/KYCElements/OrientationVideoCaptureEnhanced.d.ts.map +1 -1
  36. package/build/components/KYCElements/OrientationVideoCaptureEnhanced.js.map +1 -1
  37. package/build/components/KYCElements/OrientationVideoCaptureFinal.d.ts +2 -2
  38. package/build/components/KYCElements/OrientationVideoCaptureFinal.d.ts.map +1 -1
  39. package/build/components/KYCElements/OrientationVideoCaptureFinal.js.map +1 -1
  40. package/build/components/KYCElements/ReviewSubmitTemplate.d.ts +12 -0
  41. package/build/components/KYCElements/ReviewSubmitTemplate.d.ts.map +1 -0
  42. package/build/components/KYCElements/ReviewSubmitTemplate.js +171 -0
  43. package/build/components/KYCElements/ReviewSubmitTemplate.js.map +1 -0
  44. package/build/components/KYCElements/SelfieCaptureTemplate.d.ts +6 -2
  45. package/build/components/KYCElements/SelfieCaptureTemplate.d.ts.map +1 -1
  46. package/build/components/KYCElements/SelfieCaptureTemplate.js +105 -35
  47. package/build/components/KYCElements/SelfieCaptureTemplate.js.map +1 -1
  48. package/build/components/KYCElements/VerificationProgressTemplate.d.ts +12 -0
  49. package/build/components/KYCElements/VerificationProgressTemplate.d.ts.map +1 -0
  50. package/build/components/KYCElements/VerificationProgressTemplate.js +93 -0
  51. package/build/components/KYCElements/VerificationProgressTemplate.js.map +1 -0
  52. package/build/components/OverLay/IdCard.d.ts +1 -1
  53. package/build/components/OverLay/IdCard.d.ts.map +1 -1
  54. package/build/components/OverLay/IdCard.js +10 -6
  55. package/build/components/OverLay/IdCard.js.map +1 -1
  56. package/build/components/OverLay/SelfieOverlay.d.ts +1 -1
  57. package/build/components/OverLay/SelfieOverlay.d.ts.map +1 -1
  58. package/build/components/OverLay/SelfieOverlay.js +5 -4
  59. package/build/components/OverLay/SelfieOverlay.js.map +1 -1
  60. package/build/components/OverLay/type.d.ts +71 -1
  61. package/build/components/OverLay/type.d.ts.map +1 -1
  62. package/build/components/OverLay/type.js.map +1 -1
  63. package/build/components/TemplateKYCExample.d.ts.map +1 -1
  64. package/build/components/TemplateKYCExample.js +72 -197
  65. package/build/components/TemplateKYCExample.js.map +1 -1
  66. package/build/components/TemplateKYCFlowRefactored.d.ts.map +1 -1
  67. package/build/components/TemplateKYCFlowRefactored.js +63 -39
  68. package/build/components/TemplateKYCFlowRefactored.js.map +1 -1
  69. package/build/components/example/OrientationVideoExample.d.ts.map +1 -1
  70. package/build/components/example/OrientationVideoExample.js +1 -5
  71. package/build/components/example/OrientationVideoExample.js.map +1 -1
  72. package/build/config/countriesData.d.ts +3 -0
  73. package/build/config/countriesData.d.ts.map +1 -0
  74. package/build/config/countriesData.js +79 -0
  75. package/build/config/countriesData.js.map +1 -0
  76. package/build/config/region_mapping.d.ts +3 -0
  77. package/build/config/region_mapping.d.ts.map +1 -0
  78. package/build/config/region_mapping.js +687 -0
  79. package/build/config/region_mapping.js.map +1 -0
  80. package/build/hooks/useI18n.d.ts +11 -0
  81. package/build/hooks/useI18n.d.ts.map +1 -0
  82. package/build/hooks/useI18n.js +37 -0
  83. package/build/hooks/useI18n.js.map +1 -0
  84. package/build/hooks/useOrientationVideo.d.ts +1 -2
  85. package/build/hooks/useOrientationVideo.d.ts.map +1 -1
  86. package/build/hooks/useOrientationVideo.js +2 -1
  87. package/build/hooks/useOrientationVideo.js.map +1 -1
  88. package/build/hooks/useRealtimeVerifier.d.ts +28 -0
  89. package/build/hooks/useRealtimeVerifier.d.ts.map +1 -0
  90. package/build/hooks/useRealtimeVerifier.js +91 -0
  91. package/build/hooks/useRealtimeVerifier.js.map +1 -0
  92. package/build/hooks/useTemplateKYCFlow.d.ts +1 -0
  93. package/build/hooks/useTemplateKYCFlow.d.ts.map +1 -1
  94. package/build/hooks/useTemplateKYCFlow.js +337 -38
  95. package/build/hooks/useTemplateKYCFlow.js.map +1 -1
  96. package/build/i18n/en/index.d.ts +168 -0
  97. package/build/i18n/en/index.d.ts.map +1 -0
  98. package/build/i18n/en/index.js +195 -0
  99. package/build/i18n/en/index.js.map +1 -0
  100. package/build/i18n/fr/index.d.ts +168 -0
  101. package/build/i18n/fr/index.d.ts.map +1 -0
  102. package/build/i18n/fr/index.js +194 -0
  103. package/build/i18n/fr/index.js.map +1 -0
  104. package/build/i18n/index.d.ts +10 -0
  105. package/build/i18n/index.d.ts.map +1 -0
  106. package/build/i18n/index.js +56 -0
  107. package/build/i18n/index.js.map +1 -0
  108. package/build/i18n/types.d.ts +153 -0
  109. package/build/i18n/types.d.ts.map +1 -0
  110. package/build/i18n/types.js +3 -0
  111. package/build/i18n/types.js.map +1 -0
  112. package/build/i18n/usage-example.d.ts +4 -0
  113. package/build/i18n/usage-example.d.ts.map +1 -0
  114. package/build/i18n/usage-example.js +189 -0
  115. package/build/i18n/usage-example.js.map +1 -0
  116. package/build/modules/api/CardAuthentification.d.ts +22 -0
  117. package/build/modules/api/CardAuthentification.d.ts.map +1 -0
  118. package/build/modules/api/CardAuthentification.js +107 -0
  119. package/build/modules/api/CardAuthentification.js.map +1 -0
  120. package/build/modules/api/KYCService.d.ts +57 -1
  121. package/build/modules/api/KYCService.d.ts.map +1 -1
  122. package/build/modules/api/KYCService.js +348 -27
  123. package/build/modules/api/KYCService.js.map +1 -1
  124. package/build/modules/api/SelfieVerification.d.ts +3 -0
  125. package/build/modules/api/SelfieVerification.d.ts.map +1 -0
  126. package/build/modules/api/SelfieVerification.js +9 -0
  127. package/build/modules/api/SelfieVerification.js.map +1 -0
  128. package/build/modules/api/backendApi.d.ts +2 -0
  129. package/build/modules/api/backendApi.d.ts.map +1 -0
  130. package/build/modules/api/backendApi.js +6 -0
  131. package/build/modules/api/backendApi.js.map +1 -0
  132. package/build/modules/api/types.d.ts +20 -0
  133. package/build/modules/api/types.d.ts.map +1 -0
  134. package/build/modules/api/types.js +2 -0
  135. package/build/modules/api/types.js.map +1 -0
  136. package/build/types/KYC.types.d.ts +59 -7
  137. package/build/types/KYC.types.d.ts.map +1 -1
  138. package/build/types/KYC.types.js +9 -1
  139. package/build/types/KYC.types.js.map +1 -1
  140. package/build/utils/cropByObb.d.ts +11 -0
  141. package/build/utils/cropByObb.d.ts.map +1 -0
  142. package/build/utils/cropByObb.js +78 -0
  143. package/build/utils/cropByObb.js.map +1 -0
  144. package/build/utils/get-document-type-info.d.ts +13 -0
  145. package/build/utils/get-document-type-info.d.ts.map +1 -0
  146. package/build/utils/get-document-type-info.js +59 -0
  147. package/build/utils/get-document-type-info.js.map +1 -0
  148. package/build/utils/pathToBase64.d.ts +3 -0
  149. package/build/utils/pathToBase64.d.ts.map +1 -0
  150. package/build/utils/pathToBase64.js +47 -0
  151. package/build/utils/pathToBase64.js.map +1 -0
  152. package/build/utils/remove-duplicate.d.ts +2 -0
  153. package/build/utils/remove-duplicate.d.ts.map +1 -0
  154. package/build/utils/remove-duplicate.js +4 -0
  155. package/build/utils/remove-duplicate.js.map +1 -0
  156. package/package.json +3 -1
  157. package/src/api/axios.ts +144 -0
  158. package/src/components/EnhancedCameraView.tsx +96 -78
  159. package/src/components/EnhancedCameraView.web.tsx +41 -40
  160. package/src/components/KYCElements/CountrySelectionTemplate.tsx +104 -136
  161. package/src/components/KYCElements/FileUploadTemplate.tsx +14 -8
  162. package/src/components/KYCElements/IDCardCapture.tsx +311 -115
  163. package/src/components/KYCElements/InitializationStep.tsx +53 -0
  164. package/src/components/KYCElements/LocationCaptureTemplate.tsx +17 -15
  165. package/src/components/KYCElements/OrientationVideoCapture.tsx +2 -2
  166. package/src/components/KYCElements/OrientationVideoCaptureEnhanced.tsx +2 -2
  167. package/src/components/KYCElements/OrientationVideoCaptureFinal.tsx +2 -2
  168. package/src/components/KYCElements/ReviewSubmitTemplate.tsx +201 -0
  169. package/src/components/KYCElements/SelfieCaptureTemplate.tsx +140 -53
  170. package/src/components/KYCElements/VerificationProgressTemplate.tsx +123 -0
  171. package/src/components/OverLay/IdCard.tsx +17 -9
  172. package/src/components/OverLay/SelfieOverlay.tsx +6 -5
  173. package/src/components/OverLay/type.ts +64 -2
  174. package/src/components/TemplateKYCExample.tsx +76 -197
  175. package/src/components/TemplateKYCFlowRefactored.tsx +74 -46
  176. package/src/components/example/OrientationVideoExample.tsx +3 -7
  177. package/src/config/countriesData.ts +84 -0
  178. package/src/config/region_mapping.ts +688 -0
  179. package/src/hooks/useI18n.ts +53 -0
  180. package/src/hooks/useOrientationVideo.ts +2 -2
  181. package/src/hooks/useRealtimeVerifier.ts +128 -0
  182. package/src/hooks/useTemplateKYCFlow.tsx +375 -53
  183. package/src/i18n/README.md +288 -0
  184. package/src/i18n/en/index.ts +206 -0
  185. package/src/i18n/fr/index.ts +205 -0
  186. package/src/i18n/index.ts +65 -0
  187. package/src/i18n/types.ts +172 -0
  188. package/src/i18n/usage-example.tsx +202 -0
  189. package/src/modules/api/CardAuthentification.ts +114 -0
  190. package/src/modules/api/KYCService.ts +403 -30
  191. package/src/modules/api/SelfieVerification.ts +11 -0
  192. package/src/modules/api/backendApi.ts +8 -0
  193. package/src/modules/api/types.ts +24 -0
  194. package/src/types/KYC.types.ts +83 -14
  195. package/src/utils/cropByObb.ts +99 -0
  196. package/src/utils/get-document-type-info.ts +62 -0
  197. package/src/utils/pathToBase64.ts +47 -0
  198. package/src/utils/remove-duplicate.ts +3 -0
  199. package/src/types/nativewind.d.ts +0 -2
@@ -1,15 +1,29 @@
1
- import React, { useEffect, useState } from 'react';
2
- import { View, Text, TouchableOpacity, StyleSheet, Image, Alert, ScrollView } from 'react-native';
1
+ import React, { useCallback, useEffect, useMemo, useState } from 'react';
2
+ import { View, Text, TouchableOpacity, StyleSheet, Image, Alert, ScrollView, Dimensions } from 'react-native';
3
3
  import { EnhancedCameraView } from '../EnhancedCameraView';
4
- import { TemplateComponent, IDCardConfig, LocalizedText, GovernmentDocumentType } from '../../types/KYC.types';
4
+ import { TemplateComponent, IDCardConfig, LocalizedText, GovernmentDocumentType, ISilentCaptureResult } from '../../types/KYC.types';
5
5
  import IdCardOverlay from '../OverLay/IdCard';
6
6
  import { useTemplateKYCFlowContext } from '../../hooks/useTemplateKYCFlow';
7
+ import { useI18n } from '../../hooks/useI18n';
7
8
  import { Button } from '../ui/Button';
9
+ import { removeDuplicates } from '../../utils/remove-duplicate';
10
+ import { backVerification, frontVerification } from '../../modules/api/CardAuthentification';
11
+ import { getDocumentTypeInfo } from '../../utils/get-document-type-info';
12
+ import pathToBase64 from '../../utils/pathToBase64';
13
+ import { truncateFields } from '../../modules/api/KYCService';
14
+ import { cropImageWithBBox } from '../../utils/cropByObb';
15
+
16
+
17
+ interface IIDCardPayload {
18
+ dir: string;
19
+ file: string;
20
+ mrz: string;
21
+ }
8
22
 
9
23
  interface IDCardCaptureProps {
10
24
  component: TemplateComponent;
11
- value?: Record<string, string>;
12
- onValueChange: (value: Record<string, string>) => void;
25
+ value?: Record<string, IIDCardPayload>;
26
+ onValueChange: (value: Record<string, IIDCardPayload | string>) => void;
13
27
  error?: string;
14
28
  language?: string;
15
29
  currentSide?: string; // Nouveau prop pour spécifier le côté actuel
@@ -21,86 +35,78 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
21
35
  onValueChange,
22
36
  error,
23
37
  language = 'en',
24
- currentSide = 'front',
25
38
  }) => {
39
+ const { t } = useI18n();
26
40
  const [showCamera, setShowCamera] = useState(false);
27
- const [capturedImages, setCapturedImages] = useState<Record<string, string>>(value);
28
- const [selectedDocumentType, setSelectedDocumentType] = useState<GovernmentDocumentType | null>(null);
29
- const [showDocumentSelection, setShowDocumentSelection] = useState(true);
41
+ const [capturedImages, setCapturedImages] = useState<Record<string, IIDCardPayload>>(value || {});
42
+ const [cropImageUri, setCropImageUri] = useState<string>('');
43
+ const [currentSide, setCurrentSide] = useState<string>('front');
44
+ const [selectedDocumentType, setSelectedDocumentType] = useState<{
45
+ type: GovernmentDocumentType;
46
+ region: string;
47
+ }>({
48
+ type: 'identity_card',
49
+ region: 'root'
50
+ });
51
+ const [showDocumentSelection, setShowDocumentSelection] = useState(
52
+ {
53
+ documentSelection: true,
54
+ regionSelection: false
55
+ }
56
+ );
57
+ const [silentCaptureResult, setSilentCaptureResult] = useState<ISilentCaptureResult>({ success: false, isAnalyzing: false });
58
+ // const [imageNaturalSize, setImageNaturalSize] = useState<{ width: number; height: number } | null>(null);
30
59
 
31
60
  const { actions, state, } = useTemplateKYCFlowContext();
32
61
  const config = component.config as IDCardConfig;
33
62
 
34
- const getLocalizedText = (text: LocalizedText): string => {
35
- return text[language] || text.en || '';
36
- };
63
+
37
64
 
38
- const getDocumentTypeInfo = (docType: GovernmentDocumentType) => {
39
- switch (docType) {
40
- case 'id_card':
41
- return {
42
- name: { en: 'ID Card', fr: 'Carte d\'identité' },
43
- icon: '🆔',
44
- instructions: { en: 'Position your ID card within the frame', fr: 'Positionnez votre carte d\'identité dans le cadre' }
45
- };
46
- case 'passport':
47
- return {
48
- name: { en: 'Passport', fr: 'Passeport' },
49
- icon: '📘',
50
- instructions: { en: 'Position your passport within the frame', fr: 'Positionnez votre passeport dans le cadre' }
51
- };
52
- case 'drivers_license':
53
- return {
54
- name: { en: 'Driver\'s License', fr: 'Permis de conduire' },
55
- icon: '🚗',
56
- instructions: { en: 'Position your driver\'s license within the frame', fr: 'Positionnez votre permis de conduire dans le cadre' }
57
- };
58
- case 'residence_permit':
59
- return {
60
- name: { en: 'Residence Permit', fr: 'Permis de séjour' },
61
- icon: '🏠',
62
- instructions: { en: 'Position your residence permit within the frame', fr: 'Positionnez votre permis de séjour dans le cadre' }
63
- };
64
- case 'national_id':
65
- return {
66
- name: { en: 'National ID', fr: 'Carte nationale d\'identité' },
67
- icon: '🏛️',
68
- instructions: { en: 'Position your national ID within the frame', fr: 'Positionnez votre carte nationale d\'identité dans le cadre' }
69
- };
70
- case 'voter_id':
71
- return {
72
- name: { en: 'Voter ID', fr: 'Carte d\'électeur' },
73
- icon: '🗳️',
74
- instructions: { en: 'Position your voter ID within the frame', fr: 'Positionnez votre carte d\'électeur dans le cadre' }
75
- };
76
- case 'military_id':
77
- return {
78
- name: { en: 'Military ID', fr: 'Carte militaire' },
79
- icon: '🎖️',
80
- instructions: { en: 'Position your military ID within the frame', fr: 'Positionnez votre carte militaire dans le cadre' }
81
- };
82
- case 'student_id':
83
- return {
84
- name: { en: 'Student ID', fr: 'Carte d\'étudiant' },
85
- icon: '🎓',
86
- instructions: { en: 'Position your student ID within the frame', fr: 'Positionnez votre carte d\'étudiant dans le cadre' }
87
- };
88
- case 'work_permit':
89
- return {
90
- name: { en: 'Work Permit', fr: 'Permis de travail' },
91
- icon: '💼',
92
- instructions: { en: 'Position your work permit within the frame', fr: 'Positionnez votre permis de travail dans le cadre' }
93
- };
94
- case 'other':
95
- default:
96
- return {
97
- name: { en: 'Government Document', fr: 'Document gouvernemental' },
98
- icon: '📄',
99
- instructions: { en: 'Position your document within the frame', fr: 'Positionnez votre document dans le cadre' }
100
- };
65
+ const getLocalizedText = (text: LocalizedText | Record<string, LocalizedText>): string => {
66
+ // console.log("text", text, JSON.stringify(component, null, 2));
67
+ if (text && typeof text[currentSide] === 'object' && text[currentSide][language]) {
68
+ return text[currentSide][language] || '';
101
69
  }
70
+ return "";
102
71
  };
103
72
 
73
+ const countryData = useMemo(() => {
74
+ const geCountryID = Object.keys(state.componentData).find((c: string) => c === "6");
75
+
76
+ if (geCountryID) {
77
+ const countryMapping = state.componentData[geCountryID];
78
+ return countryMapping;
79
+ }
80
+ return null;
81
+ }, [state.componentData]);
82
+
83
+
84
+ const availableDocumentTypes = useMemo(() => {
85
+ const newconfig: GovernmentDocumentType[] = [];
86
+
87
+ if (countryData) {
88
+ const countryMapping = Object.keys(countryData?.regionMapping);
89
+ if (countryMapping && Array.isArray(countryMapping)) {
90
+ countryMapping.forEach((c) => {
91
+ newconfig.push(c as GovernmentDocumentType);
92
+ });
93
+ }
94
+ }
95
+ console.log("newconfig", truncateFields(newconfig));
96
+ return newconfig;
97
+ }, []);
98
+
99
+
100
+ useEffect(() => {
101
+ console.log("cropImageUri", JSON.stringify(truncateFields({ box: silentCaptureResult }), null, 2));
102
+ if (capturedImages[currentSide]?.dir && silentCaptureResult?.bbox) {
103
+ cropImageWithBBox(capturedImages[currentSide].dir, silentCaptureResult.bbox)?.then((uri) => {
104
+ setCropImageUri(uri);
105
+ });
106
+ }
107
+ }, [capturedImages[currentSide]?.dir, silentCaptureResult?.bbox])
108
+
109
+
104
110
 
105
111
  const cameraConfig = {
106
112
  aspectRatio: 4 / 3,
@@ -111,8 +117,8 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
111
117
  maxRetakes: 3,
112
118
  overlay: {
113
119
  showGuide: true,
114
- guideText: selectedDocumentType ? getDocumentTypeInfo(selectedDocumentType).instructions.en : getLocalizedText(component.instructions),
115
- bbox: selectedDocumentType && config.bbox_configs[selectedDocumentType] ? config.bbox_configs[selectedDocumentType] : {
120
+ guideText: selectedDocumentType ? getDocumentTypeInfo(selectedDocumentType.type).instructions.en : getLocalizedText(component.instructions as Record<string, LocalizedText>),
121
+ bbox: selectedDocumentType && config.bbox_configs[selectedDocumentType.type] ? config.bbox_configs[selectedDocumentType.type] : {
116
122
  xMin: 20,
117
123
  yMin: 140,
118
124
  xMax: 370,
@@ -128,15 +134,118 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
128
134
  const retakePicture = (currentSide: string) => {
129
135
  setShowCamera(true);
130
136
  actions.showCustomStepper(false);
131
- setCapturedImages({ ...capturedImages, [currentSide]: '' });
132
- onValueChange({ ...value, [currentSide]: '' });
137
+ setCapturedImages({ ...capturedImages, [currentSide]: { dir: '', file: '', mrz: '' } });
138
+ setSilentCaptureResult((prev) => ({ path: '', success: false, isAnalyzing: false, error: '' }));
139
+ setCropImageUri('');
140
+ onValueChange({ ...value, [currentSide]: { dir: '', file: '', mrz: '' } });
133
141
  };
134
142
 
135
- const handleCapture = (result: { success: boolean; path?: string; error?: string }) => {
143
+ const hasRegions = useCallback((documentType: GovernmentDocumentType): { hasRegions: boolean, regionMapping: string[] } => {
144
+ const regionMapping = countryData?.regionMapping[documentType as GovernmentDocumentType];
145
+ if (regionMapping && Object.keys(regionMapping).length > 1) {
146
+ return {
147
+ hasRegions: true,
148
+ regionMapping: Object.keys(regionMapping)
149
+ };
150
+ }
151
+ return {
152
+ hasRegions: false,
153
+ regionMapping: []
154
+ };
155
+ }, [countryData]);
156
+
157
+
158
+ const getCurrentSideVerification = (currentSide: string) => {
159
+ const regionMapping = countryData?.regionMapping[selectedDocumentType?.type as GovernmentDocumentType];
160
+ const authMethod: string[] = [];
161
+
162
+ const key = selectedDocumentType?.region?.trim()?.length > 0 ? selectedDocumentType?.region?.trim() : 'root';
163
+ // console.log("regionMapping", JSON.stringify(regionMapping, null, 2), selectedDocumentType?.region);
164
+ // const key = selectedDocumentType?.region as keyof typeof regionMapping;
165
+
166
+ if (regionMapping?.[key] && Array.isArray(regionMapping[key])) {
167
+ regionMapping[key].forEach((item: any) => {
168
+ if (item[currentSide as keyof typeof item]) {
169
+ authMethod.push(item[currentSide as keyof typeof item]);
170
+ }
171
+ });
172
+ }
173
+
174
+ console.log("regionMapping", JSON.stringify(truncateFields({ regionMapping, selectedDocumentType: selectedDocumentType?.region, key }), null, 2));
175
+
176
+ return removeDuplicates(authMethod);
177
+ }
178
+
179
+
180
+ const handleSilentCapture = (result: { success: boolean; path?: string; error?: string }) => {
181
+ if (silentCaptureResult.isAnalyzing) {
182
+ return;
183
+ }
136
184
  if (result.success && result.path) {
137
- const newImages = { ...capturedImages, [currentSide]: result.path };
185
+ setSilentCaptureResult((prev) => ({ ...prev, isAnalyzing: true, success: false, error: '' }));
186
+ if (currentSide === 'front') {
187
+ frontVerification(
188
+ {
189
+ path: result.path,
190
+ regionMapping: getCurrentSideVerification(currentSide),
191
+ selectedDocumentType: selectedDocumentType?.type || '',
192
+ code: countryData?.code || '',
193
+ currentSide: currentSide,
194
+
195
+ }).then((mrz) => {
196
+ console.log("front verification result", truncateFields(mrz));
197
+ setSilentCaptureResult((prev) => ({
198
+ ...prev, path: result.path,
199
+ bbox: (mrz as any)?.bbox, success: true,
200
+ mrz: JSON.stringify(mrz), isAnalyzing: false,
201
+ country: countryData?.code,
202
+ documentType: selectedDocumentType?.type
203
+ }),
204
+ );
205
+ }).catch((e: any) => {
206
+ console.log("error front verification", truncateFields(e));
207
+ setSilentCaptureResult((prev) => ({ ...prev, isAnalyzing: false, success: false, error: e?.message || 'Erreur de détection du MRZ' }));
208
+ // Alert.alert('Erreur', e?.message || 'Erreur de détection du MRZ');
209
+ })
210
+ } else {
211
+ backVerification({
212
+ path: result.path,
213
+ regionMapping: getCurrentSideVerification(currentSide),
214
+ selectedDocumentType: selectedDocumentType?.type || '',
215
+ code: countryData?.code || '',
216
+ currentSide: currentSide,
217
+
218
+ }).then((mrz) => {
219
+ console.log("back verification result", truncateFields(mrz));
220
+ setSilentCaptureResult((prev) => ({
221
+ ...prev, path: result.path, bbox: (mrz as any)?.bbox,
222
+ success: true, mrz: JSON.stringify(mrz), isAnalyzing: false,
223
+ country: countryData?.code,
224
+ documentType: selectedDocumentType?.type
225
+ }));
226
+ }).catch((e: any) => {
227
+ console.log("error back verification", truncateFields(e));
228
+ setSilentCaptureResult((prev) => ({ ...prev, isAnalyzing: false, success: false, error: e?.message || 'Erreur de détection du MRZ' }));
229
+ // Alert.alert('Erreur', e?.message || 'Erreur de détection du MRZ');
230
+ })
231
+ }
232
+
233
+ }
234
+ }
235
+
236
+ // Handle capture
237
+ const handleCapture = async (result: { success: boolean; path?: string; error?: string }) => {
238
+ if (silentCaptureResult.path) {
239
+ const base64 = await pathToBase64(silentCaptureResult.path);
240
+ const newImages = { ...capturedImages, [currentSide]: { dir: silentCaptureResult.path, file: base64, mrz: silentCaptureResult.mrz || "" } };
138
241
  setCapturedImages(newImages);
139
- onValueChange(newImages);
242
+ if (silentCaptureResult.country && silentCaptureResult.documentType) {
243
+ onValueChange({
244
+ ...newImages,
245
+ country: silentCaptureResult.country,
246
+ documentType: silentCaptureResult.documentType,
247
+ });
248
+ }
140
249
  setShowCamera(false);
141
250
  actions.showCustomStepper(true);
142
251
  } else {
@@ -150,19 +259,46 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
150
259
  };
151
260
 
152
261
 
153
- useEffect(() => {
154
262
 
155
- actions.showCustomStepper(!showCamera);
156
263
 
264
+
265
+ useEffect(() => {
266
+ actions.showCustomStepper(!showCamera);
157
267
  }, [showCamera]);
158
268
 
159
269
 
270
+
271
+ // const renderBboxOverlay = () => {
272
+ // const bbox: any = (silentCaptureResult as any)?.bbox;
273
+ // if (!bbox || !imageNaturalSize) return null;
274
+ // const containerWidth = Dimensions.get('window').width - 24 - 24; // approximate inner width accounting paddings/margins
275
+ // const containerHeight = 200;
276
+ // const scaleX = 1;
277
+ // const scaleY = 1;
278
+ // const left = Math.max(0, bbox.minX * scaleX);
279
+ // const top = Math.max(0, bbox.minY * scaleY);
280
+ // const width = Math.max(1, bbox.width * scaleX);
281
+ // const height = Math.max(1, bbox.height * scaleY);
282
+ // return (
283
+ // <View
284
+ // pointerEvents="none"
285
+ // style={{ position: 'absolute', left, top, width, height, borderColor: '#2DBD60', borderWidth: 2, borderStyle: 'solid', borderRadius: 6 }}
286
+ // />
287
+ // );
288
+ // };
289
+
290
+
291
+
292
+
160
293
  const handleDocumentTypeSelection = (docType: GovernmentDocumentType) => {
161
- setSelectedDocumentType(docType);
294
+ setSelectedDocumentType({ type: docType, region: hasRegions(docType) ? '' : 'root' });
295
+ };
296
+ const handleRegionSelection = (region: string) => {
297
+ setSelectedDocumentType((prev) => ({ ...prev, region: region }));
162
298
  };
163
299
 
164
300
  // Interface de sélection du type de document
165
- if (showDocumentSelection) {
301
+ if (showDocumentSelection.documentSelection || showDocumentSelection.regionSelection) {
166
302
  return (
167
303
  <View style={styles.root}>
168
304
  <View style={styles.container}>
@@ -171,12 +307,12 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
171
307
  <Text style={styles.description}>{getLocalizedText(component.instructions)}</Text>
172
308
 
173
309
  <ScrollView style={styles.documentTypesContainer} showsVerticalScrollIndicator={false}>
174
- {config.document_types.map((docType) => {
310
+ {showDocumentSelection.documentSelection ? availableDocumentTypes.map((docType) => {
175
311
  const docInfo = getDocumentTypeInfo(docType);
176
312
  return (
177
313
  <TouchableOpacity
178
314
  key={docType}
179
- style={[styles.documentTypeButton, { backgroundColor: selectedDocumentType === docType ? '#2DBD6030' : '#F8F9FA', borderColor: selectedDocumentType === docType ? '#2DBD60' : '#E9ECEF', borderWidth: 2 }]}
315
+ style={[styles.documentTypeButton, { backgroundColor: selectedDocumentType?.type === docType ? '#2DBD6030' : '#F8F9FA', borderColor: selectedDocumentType?.type === docType ? '#2DBD60' : '#E9ECEF', borderWidth: 2 }]}
180
316
  onPress={() => handleDocumentTypeSelection(docType)}
181
317
  >
182
318
  <View style={[styles.documentTypeIconContainer, { backgroundColor: 'gray', }]}>
@@ -188,21 +324,54 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
188
324
  <Text style={styles.documentTypeArrow}>→</Text>
189
325
  </TouchableOpacity>
190
326
  );
191
- })}
327
+ }) : null}
328
+ {showDocumentSelection.regionSelection ?
329
+ hasRegions(selectedDocumentType?.type || 'identity_card').regionMapping.map((region) => {
330
+ return (
331
+ <TouchableOpacity key={region} onPress={() => handleRegionSelection(region)}
332
+ style={[styles.documentTypeButton, { backgroundColor: selectedDocumentType?.region === region ? '#2DBD6030' : '#F8F9FA', borderColor: selectedDocumentType?.region === region ? '#2DBD60' : '#E9ECEF', borderWidth: 2 }]}
333
+
334
+ >
335
+ <Text>{region}</Text>
336
+ </TouchableOpacity>
337
+ );
338
+ }) : null}
192
339
  </ScrollView>
193
- <Button title={getLocalizedText(component.ui.buttonText)} onPress={() => {
340
+ <Button title={t('common.next')} onPress={() => {
194
341
  if (!selectedDocumentType) {
195
- Alert.alert('Erreur', 'Veuillez sélectionner un type de document');
342
+ Alert.alert(t('common.error'), t('validation.required'));
196
343
  return;
197
344
  }
198
- setShowDocumentSelection(false);
199
- setShowCamera(true);
200
- actions.showCustomStepper(false);
345
+
346
+ if (showDocumentSelection.regionSelection) {
347
+ console.log("showDocumentSelection", JSON.stringify(truncateFields(showDocumentSelection), null, 2));
348
+ setShowDocumentSelection({
349
+ documentSelection: false,
350
+ regionSelection: false
351
+ });
352
+ setShowCamera(true);
353
+ actions.showCustomStepper(false);
354
+ return;
355
+ } else {
356
+ if (hasRegions(selectedDocumentType?.type)?.hasRegions) {
357
+ setShowDocumentSelection({
358
+ documentSelection: false,
359
+ regionSelection: true
360
+ });
361
+ } else {
362
+ setShowDocumentSelection({
363
+ documentSelection: false,
364
+ regionSelection: false
365
+ });
366
+ setShowCamera(true);
367
+ actions.showCustomStepper(false);
368
+ }
369
+ }
370
+
201
371
  }}
202
372
  variant={selectedDocumentType ? 'primary' : "neutral"}
203
373
  size="large"
204
374
  fullWidth
205
- // rounded
206
375
  />
207
376
 
208
377
 
@@ -230,6 +399,8 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
230
399
  quality="high"
231
400
  showCaptureButton={true}
232
401
  showSwitchCamera={true}
402
+ onSilentCapture={handleSilentCapture}
403
+ silentCaptureResult={silentCaptureResult}
233
404
  enableFlash={cameraConfig.flashMode === 'auto' || cameraConfig.flashMode === 'on'}
234
405
  overlayComponent={<IdCardOverlay
235
406
  xMin={cameraConfig.overlay.bbox.xMin}
@@ -238,12 +409,27 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
238
409
  yMax={cameraConfig.overlay.bbox.yMax}
239
410
  instructions={cameraConfig.overlay.guideText}
240
411
  cornerOpacity={cameraConfig.overlay.bbox.cornerRadius || 0 as number}
412
+ isSuccess={silentCaptureResult.success}
413
+ language={state.currentLanguage}
241
414
  stepperProps={{
242
415
  back: () => {
243
- setShowDocumentSelection(true);
244
- actions.showCustomStepper(false);
416
+ if (currentSide === 'back') {
417
+ setCurrentSide('front');
418
+ setShowCamera(false);
419
+ setShowDocumentSelection({
420
+ documentSelection: false,
421
+ regionSelection: false
422
+ });
423
+ setSilentCaptureResult((prev) => ({ path: '', success: false, isAnalyzing: false, error: '' }));
424
+ } else {
425
+ setShowDocumentSelection({
426
+ documentSelection: true,
427
+ regionSelection: false
428
+ });
429
+ actions.showCustomStepper(true);
430
+ }
245
431
  },
246
- selectedDocumentType: getDocumentTypeInfo(selectedDocumentType || 'id_card').name.en || '',
432
+ selectedDocumentType: getDocumentTypeInfo(selectedDocumentType?.type || "identity_card").name.en || '',
247
433
  step: state.currentComponentIndex + 1,
248
434
  totalSteps: state.template.components.length,
249
435
  }}
@@ -254,6 +440,7 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
254
440
  );
255
441
  }
256
442
 
443
+
257
444
  return (
258
445
  <View style={styles.root}>
259
446
  <View style={styles.previewContainer}>
@@ -266,7 +453,6 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
266
453
  {getLocalizedText(component.instructions)}
267
454
  </Text>
268
455
 
269
-
270
456
  <View style={{ alignItems: 'center', justifyContent: 'center', flexDirection: "column", gap: 16 }}>
271
457
  <View
272
458
  style={{
@@ -285,29 +471,38 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
285
471
  backgroundColor: '#fff', // Needed for shadow to show on iOS
286
472
  }}
287
473
  >
288
- <Image
289
- source={{ uri: capturedImages[currentSide] }}
290
- style={{
291
- width: '100%',
292
- height: 200,
293
- borderRadius: 12,
294
- resizeMode: 'cover',
295
- }}
296
- />
474
+ {cropImageUri ? (
475
+ <Image
476
+ source={{ uri: cropImageUri }}
477
+ style={{
478
+ width: '100%',
479
+ height: 200,
480
+ borderRadius: 12,
481
+ resizeMode: 'cover',
482
+ }}
483
+ />
484
+ ) : null}
485
+
297
486
  </View>
298
487
  {/* retake button */}
299
- <Button title="Reprendre" onPress={() => {
488
+ <Button title={t('kyc.idCardCapture.retakeButton')} onPress={() => {
300
489
  retakePicture(currentSide);
301
490
  }}
302
491
  variant="outline"
303
492
  size="medium"
304
493
  fullWidth
305
-
306
- />
307
- <Button title="Suivant" onPress={() => {
308
- setShowCamera(true);
309
494
 
310
- actions.nextComponent();
495
+ />
496
+ <Button title={t('common.next')} onPress={() => {
497
+ if (currentSide === 'back' || selectedDocumentType?.type === 'passport') {
498
+ actions.nextComponent();
499
+ return;
500
+ } else {
501
+ setShowCamera(true);
502
+ setCurrentSide('back');
503
+ setSilentCaptureResult((prev) => ({ path: '', success: false, isAnalyzing: false, error: '', mrz: '' }));
504
+ setCropImageUri('');
505
+ }
311
506
  }}
312
507
  variant="primary"
313
508
  size="large"
@@ -480,8 +675,9 @@ const styles = StyleSheet.create({
480
675
  },
481
676
  documentTypesContainer: {
482
677
  // flex: 1,
483
- marginBottom: 16,
678
+ // marginBottom: 16,
484
679
  // paddingHorizontal: 16,
680
+ maxHeight: Dimensions.get('window').height - 400,
485
681
  },
486
682
  documentTypeButton: {
487
683
  flexDirection: 'row',
@@ -0,0 +1,53 @@
1
+ import { ActivityIndicator, Button, StyleSheet, Text, View } from "react-native"
2
+ import { SessionState } from "../../types/KYC.types"
3
+ import { useEffect } from "react"
4
+ import { useTemplateKYCFlowContext } from "../../hooks/useTemplateKYCFlow"
5
+ import { useI18n } from "../../hooks/useI18n"
6
+
7
+ export const InitializationStep = ({ session }: { session: SessionState }) => {
8
+ const { t } = useI18n();
9
+ const { initializeSession } = useTemplateKYCFlowContext()
10
+ useEffect(() => {
11
+ initializeSession()
12
+ }, [])
13
+
14
+
15
+
16
+ if (session.error) {
17
+ return (
18
+ <View style={styles.container}>
19
+ <Text style={styles.errorText}>{t('errors.serverError')}</Text>
20
+ <Button title={t('common.retry')} onPress={initializeSession} />
21
+ </View>
22
+ )
23
+ }
24
+
25
+ return (
26
+ <View style={styles.container}>
27
+ <Text style={styles.initializingText}>{t('common.loading')}</Text>
28
+ {session.isProcessing && (
29
+ <ActivityIndicator size="large" color="#0000ff" />
30
+ )}
31
+ </View>
32
+ )
33
+ }
34
+
35
+ const styles = StyleSheet.create({
36
+ container: {
37
+ flex: 1,
38
+ justifyContent: 'center',
39
+ alignItems: 'center',
40
+ },
41
+ errorText: {
42
+ color: '#dc2626',
43
+ fontSize: 16,
44
+ textAlign: 'center',
45
+ margin: 20,
46
+ },
47
+ initializingText: {
48
+ color: '#0000ff',
49
+ fontSize: 16,
50
+ textAlign: 'center',
51
+ margin: 20,
52
+ },
53
+ })