@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.
- package/android/build/intermediates/aapt_friendly_merged_manifests/debug/processDebugManifest/aapt/AndroidManifest.xml +12 -5
- package/android/build/intermediates/aar_main_jar/debug/syncDebugLibJars/classes.jar +0 -0
- package/android/build/intermediates/annotations_typedef_file/debug/extractDebugAnnotations/typedefs.txt +0 -0
- package/android/build/intermediates/incremental/debug/packageDebugResources/compile-file-map.properties +1 -1
- package/android/build/intermediates/incremental/debug-mergeJavaRes/merge-state +0 -0
- package/android/build/intermediates/manifest_merge_blame_file/debug/processDebugManifest/manifest-merger-blame-debug-report.txt +61 -59
- package/android/build/intermediates/merged_java_res/debug/mergeDebugJavaResource/feature-transfergratis-react-native-sdk.jar +0 -0
- package/android/build/intermediates/merged_manifest/debug/processDebugManifest/AndroidManifest.xml +12 -5
- package/android/build/kotlin/compileDebugKotlin/cacheable/last-build.bin +0 -0
- package/android/build/kotlin/compileDebugKotlin/local-state/build-history.bin +0 -0
- package/android/build/outputs/aar/transfergratis-react-native-sdk-debug.aar +0 -0
- package/android/build/outputs/logs/manifest-merger-debug-report.txt +26 -34
- package/android/src/main/AndroidManifest.xml +13 -5
- package/build/components/EnhancedCameraView.d.ts.map +1 -1
- package/build/components/EnhancedCameraView.js +26 -3
- package/build/components/EnhancedCameraView.js.map +1 -1
- package/build/components/EnhancedCameraView.web.d.ts.map +1 -1
- package/build/components/EnhancedCameraView.web.js +21 -0
- package/build/components/EnhancedCameraView.web.js.map +1 -1
- package/build/components/KYCElements/AdditionalDocumentsTemplate.d.ts +12 -0
- package/build/components/KYCElements/AdditionalDocumentsTemplate.d.ts.map +1 -0
- package/build/components/KYCElements/AdditionalDocumentsTemplate.js +283 -0
- package/build/components/KYCElements/AdditionalDocumentsTemplate.js.map +1 -0
- package/build/components/KYCElements/CameraCapture.d.ts.map +1 -1
- package/build/components/KYCElements/CameraCapture.js +4 -3
- package/build/components/KYCElements/CameraCapture.js.map +1 -1
- package/build/components/KYCElements/CountrySelectionTemplate.d.ts +5 -2
- package/build/components/KYCElements/CountrySelectionTemplate.d.ts.map +1 -1
- package/build/components/KYCElements/CountrySelectionTemplate.js +360 -101
- package/build/components/KYCElements/CountrySelectionTemplate.js.map +1 -1
- package/build/components/KYCElements/EmailVerificationTemplate.d.ts +12 -0
- package/build/components/KYCElements/EmailVerificationTemplate.d.ts.map +1 -0
- package/build/components/KYCElements/EmailVerificationTemplate.js +193 -0
- package/build/components/KYCElements/EmailVerificationTemplate.js.map +1 -0
- package/build/components/KYCElements/FileUpload.d.ts.map +1 -1
- package/build/components/KYCElements/FileUpload.js +5 -4
- package/build/components/KYCElements/FileUpload.js.map +1 -1
- package/build/components/KYCElements/FileUploadTemplate.d.ts.map +1 -1
- package/build/components/KYCElements/FileUploadTemplate.js +5 -4
- package/build/components/KYCElements/FileUploadTemplate.js.map +1 -1
- package/build/components/KYCElements/IDCardCapture.d.ts.map +1 -1
- package/build/components/KYCElements/IDCardCapture.js +356 -227
- package/build/components/KYCElements/IDCardCapture.js.map +1 -1
- package/build/components/KYCElements/LocationCaptureTemplate.d.ts.map +1 -1
- package/build/components/KYCElements/LocationCaptureTemplate.js +78 -37
- package/build/components/KYCElements/LocationCaptureTemplate.js.map +1 -1
- package/build/components/KYCElements/OrientationVideoCapture.d.ts +2 -0
- package/build/components/KYCElements/OrientationVideoCapture.d.ts.map +1 -1
- package/build/components/KYCElements/OrientationVideoCapture.js +5 -4
- package/build/components/KYCElements/OrientationVideoCapture.js.map +1 -1
- package/build/components/KYCElements/OrientationVideoCaptureEnhanced.d.ts +2 -0
- package/build/components/KYCElements/OrientationVideoCaptureEnhanced.d.ts.map +1 -1
- package/build/components/KYCElements/OrientationVideoCaptureEnhanced.js +5 -4
- package/build/components/KYCElements/OrientationVideoCaptureEnhanced.js.map +1 -1
- package/build/components/KYCElements/OrientationVideoCaptureFinal.d.ts +2 -0
- package/build/components/KYCElements/OrientationVideoCaptureFinal.d.ts.map +1 -1
- package/build/components/KYCElements/OrientationVideoCaptureFinal.js +5 -4
- package/build/components/KYCElements/OrientationVideoCaptureFinal.js.map +1 -1
- package/build/components/KYCElements/PersonalInformationTemplate.d.ts +12 -0
- package/build/components/KYCElements/PersonalInformationTemplate.d.ts.map +1 -0
- package/build/components/KYCElements/PersonalInformationTemplate.js +120 -0
- package/build/components/KYCElements/PersonalInformationTemplate.js.map +1 -0
- package/build/components/KYCElements/PhoneVerificationTemplate.d.ts +12 -0
- package/build/components/KYCElements/PhoneVerificationTemplate.d.ts.map +1 -0
- package/build/components/KYCElements/PhoneVerificationTemplate.js +185 -0
- package/build/components/KYCElements/PhoneVerificationTemplate.js.map +1 -0
- package/build/components/KYCElements/SelfieCapture.d.ts.map +1 -1
- package/build/components/KYCElements/SelfieCapture.js +4 -3
- package/build/components/KYCElements/SelfieCapture.js.map +1 -1
- package/build/components/KYCElements/SelfieCaptureTemplate.d.ts.map +1 -1
- package/build/components/KYCElements/SelfieCaptureTemplate.js +189 -42
- package/build/components/KYCElements/SelfieCaptureTemplate.js.map +1 -1
- package/build/components/KYCElements/WelcomeTemplate.d.ts +12 -0
- package/build/components/KYCElements/WelcomeTemplate.d.ts.map +1 -0
- package/build/components/KYCElements/WelcomeTemplate.js +243 -0
- package/build/components/KYCElements/WelcomeTemplate.js.map +1 -0
- package/build/components/TemplateKYCExample.d.ts +8 -2
- package/build/components/TemplateKYCExample.d.ts.map +1 -1
- package/build/components/TemplateKYCExample.js +10 -97
- package/build/components/TemplateKYCExample.js.map +1 -1
- package/build/components/TemplateKYCFlowRefactored.d.ts +6 -1
- package/build/components/TemplateKYCFlowRefactored.d.ts.map +1 -1
- package/build/components/TemplateKYCFlowRefactored.js +108 -11
- package/build/components/TemplateKYCFlowRefactored.js.map +1 -1
- package/build/components/example/DynamicTemplateExample.d.ts +10 -0
- package/build/components/example/DynamicTemplateExample.d.ts.map +1 -0
- package/build/components/example/DynamicTemplateExample.js +241 -0
- package/build/components/example/DynamicTemplateExample.js.map +1 -0
- package/build/config/KYCConfig.d.ts +14 -0
- package/build/config/KYCConfig.d.ts.map +1 -0
- package/build/config/KYCConfig.js +26 -0
- package/build/config/KYCConfig.js.map +1 -0
- package/build/config/allowedDomains.d.ts +30 -0
- package/build/config/allowedDomains.d.ts.map +1 -0
- package/build/config/allowedDomains.js +112 -0
- package/build/config/allowedDomains.js.map +1 -0
- package/build/hooks/useOrientationVideo.d.ts +2 -1
- package/build/hooks/useOrientationVideo.d.ts.map +1 -1
- package/build/hooks/useOrientationVideo.js +3 -3
- package/build/hooks/useOrientationVideo.js.map +1 -1
- package/build/hooks/useTemplateKYCFlow.d.ts +6 -1
- package/build/hooks/useTemplateKYCFlow.d.ts.map +1 -1
- package/build/hooks/useTemplateKYCFlow.js +317 -34
- package/build/hooks/useTemplateKYCFlow.js.map +1 -1
- package/build/hooks/useTemplateLoader.d.ts +14 -0
- package/build/hooks/useTemplateLoader.d.ts.map +1 -0
- package/build/hooks/useTemplateLoader.js +85 -0
- package/build/hooks/useTemplateLoader.js.map +1 -0
- package/build/i18n/en/index.d.ts +49 -0
- package/build/i18n/en/index.d.ts.map +1 -1
- package/build/i18n/en/index.js +50 -1
- package/build/i18n/en/index.js.map +1 -1
- package/build/i18n/fr/index.d.ts +35 -0
- package/build/i18n/fr/index.d.ts.map +1 -1
- package/build/i18n/fr/index.js +36 -1
- package/build/i18n/fr/index.js.map +1 -1
- package/build/index.d.ts +6 -0
- package/build/index.d.ts.map +1 -1
- package/build/index.js +10 -0
- package/build/index.js.map +1 -1
- package/build/modules/api/CardAuthentification.d.ts +24 -3
- package/build/modules/api/CardAuthentification.d.ts.map +1 -1
- package/build/modules/api/CardAuthentification.js +69 -10
- package/build/modules/api/CardAuthentification.js.map +1 -1
- package/build/modules/api/KYCService.d.ts +7 -7
- package/build/modules/api/KYCService.d.ts.map +1 -1
- package/build/modules/api/KYCService.js +108 -39
- package/build/modules/api/KYCService.js.map +1 -1
- package/build/modules/api/SelfieVerification.d.ts +3 -1
- package/build/modules/api/SelfieVerification.d.ts.map +1 -1
- package/build/modules/api/SelfieVerification.js +17 -1
- package/build/modules/api/SelfieVerification.js.map +1 -1
- package/build/modules/api/TemplateService.d.ts +44 -0
- package/build/modules/api/TemplateService.d.ts.map +1 -0
- package/build/modules/api/TemplateService.js +145 -0
- package/build/modules/api/TemplateService.js.map +1 -0
- package/build/types/KYC.types.d.ts +265 -4
- package/build/types/KYC.types.d.ts.map +1 -1
- package/build/types/KYC.types.js +15 -0
- package/build/types/KYC.types.js.map +1 -1
- package/build/types/env.types.d.ts +13 -0
- package/build/types/env.types.d.ts.map +1 -0
- package/build/types/env.types.js +2 -0
- package/build/types/env.types.js.map +1 -0
- package/build/utils/cropByObb.d.ts +1 -0
- package/build/utils/cropByObb.d.ts.map +1 -1
- package/build/utils/cropByObb.js +70 -0
- package/build/utils/cropByObb.js.map +1 -1
- package/build/utils/deviceDetection.d.ts +6 -0
- package/build/utils/deviceDetection.d.ts.map +1 -0
- package/build/utils/deviceDetection.js +12 -0
- package/build/utils/deviceDetection.js.map +1 -0
- package/build/utils/platformAlert.d.ts +20 -0
- package/build/utils/platformAlert.d.ts.map +1 -0
- package/build/utils/platformAlert.js +67 -0
- package/build/utils/platformAlert.js.map +1 -0
- package/build/utils/template-transformer.d.ts +10 -0
- package/build/utils/template-transformer.d.ts.map +1 -0
- package/build/utils/template-transformer.js +365 -0
- package/build/utils/template-transformer.js.map +1 -0
- package/build/web/WebKYCEntry.d.ts.map +1 -1
- package/build/web/WebKYCEntry.js +158 -32
- package/build/web/WebKYCEntry.js.map +1 -1
- package/package.json +1 -1
- package/plugin/build/withVisionCamera.js +3 -4
- package/plugin/src/withVisionCamera.js +3 -4
- package/plugin/src/withVisionCamera.ts +3 -4
- package/src/components/EnhancedCameraView.tsx +31 -2
- package/src/components/EnhancedCameraView.web.tsx +24 -0
- package/src/components/KYCElements/AdditionalDocumentsTemplate.tsx +346 -0
- package/src/components/KYCElements/CameraCapture.tsx +4 -3
- package/src/components/KYCElements/CountrySelectionTemplate.tsx +410 -113
- package/src/components/KYCElements/EmailVerificationTemplate.tsx +264 -0
- package/src/components/KYCElements/FileUpload.tsx +5 -4
- package/src/components/KYCElements/FileUploadTemplate.tsx +5 -4
- package/src/components/KYCElements/IDCardCapture.tsx +397 -254
- package/src/components/KYCElements/LocationCaptureTemplate.tsx +95 -44
- package/src/components/KYCElements/OrientationVideoCapture.tsx +6 -3
- package/src/components/KYCElements/OrientationVideoCaptureEnhanced.tsx +6 -3
- package/src/components/KYCElements/OrientationVideoCaptureFinal.tsx +6 -3
- package/src/components/KYCElements/PersonalInformationTemplate.tsx +158 -0
- package/src/components/KYCElements/PhoneVerificationTemplate.tsx +253 -0
- package/src/components/KYCElements/SelfieCapture.tsx +4 -3
- package/src/components/KYCElements/SelfieCaptureTemplate.tsx +201 -44
- package/src/components/KYCElements/WelcomeTemplate.tsx +289 -0
- package/src/components/TemplateKYCExample.tsx +37 -108
- package/src/components/TemplateKYCFlowRefactored.tsx +148 -12
- package/src/components/example/DynamicTemplateExample.tsx +289 -0
- package/src/config/KYCConfig.ts +34 -0
- package/src/config/allowedDomains.ts +133 -0
- package/src/hooks/useOrientationVideo.ts +5 -4
- package/src/hooks/useTemplateKYCFlow.tsx +347 -32
- package/src/hooks/useTemplateLoader.ts +102 -0
- package/src/i18n/en/index.ts +53 -2
- package/src/i18n/fr/index.ts +37 -1
- package/src/index.ts +14 -0
- package/src/modules/api/CardAuthentification.ts +76 -11
- package/src/modules/api/KYCService.ts +129 -45
- package/src/modules/api/SelfieVerification.ts +25 -3
- package/src/modules/api/TemplateService.ts +167 -0
- package/src/types/KYC.types.ts +331 -3
- package/src/types/env.types.ts +13 -0
- package/src/utils/cropByObb.ts +83 -3
- package/src/utils/deviceDetection.ts +11 -0
- package/src/utils/platformAlert.ts +86 -0
- package/src/utils/template-transformer.ts +445 -0
- package/src/web/WebKYCEntry.tsx +199 -50
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import React, {
|
|
2
|
-
import { View, Text,
|
|
1
|
+
import React, { useEffect, useMemo, useState } from 'react';
|
|
2
|
+
import { View, Text, StyleSheet, Image, ScrollView, Platform, Modal, TouchableOpacity } from 'react-native';
|
|
3
|
+
import { showAlert } from '../../utils/platformAlert';
|
|
3
4
|
import { EnhancedCameraView } from '../EnhancedCameraView';
|
|
4
|
-
import { TemplateComponent,
|
|
5
|
+
import { TemplateComponent, LocalizedText, GovernmentDocumentType, ISilentCaptureResult, IBbox, GovernmentDocumentTypeShorted, GovernmentDocumentTypeBackend } from '../../types/KYC.types';
|
|
5
6
|
import IdCardOverlay from '../OverLay/IdCard';
|
|
6
7
|
import { useTemplateKYCFlowContext } from '../../hooks/useTemplateKYCFlow';
|
|
7
8
|
import { useI18n } from '../../hooks/useI18n';
|
|
@@ -10,8 +11,9 @@ import { removeDuplicates } from '../../utils/remove-duplicate';
|
|
|
10
11
|
import { backVerification, checkTemplateType, frontVerification } from '../../modules/api/CardAuthentification';
|
|
11
12
|
import { getDocumentTypeInfo } from '../../utils/get-document-type-info';
|
|
12
13
|
import pathToBase64 from '../../utils/pathToBase64';
|
|
13
|
-
import { truncateFields } from '../../modules/api/KYCService';
|
|
14
|
-
import { cropByObb, cropImageWithBBox } from '../../utils/cropByObb';
|
|
14
|
+
import kycService, { truncateFields } from '../../modules/api/KYCService';
|
|
15
|
+
import { cropByObb, cropImageWithBBox, cropImageWithBBoxWithTolerance } from '../../utils/cropByObb';
|
|
16
|
+
import { isMobileWeb } from '../../utils/deviceDetection';
|
|
15
17
|
import { logger } from '../../utils/logger';
|
|
16
18
|
|
|
17
19
|
|
|
@@ -45,24 +47,20 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
|
|
|
45
47
|
const [currentSide, setCurrentSide] = useState<string>('front');
|
|
46
48
|
// Stocker les bbox par côté pour pouvoir restaurer les images croppées
|
|
47
49
|
const [bboxBySide, setBboxBySide] = useState<Record<string, IBbox>>({});
|
|
48
|
-
const [selectedDocumentType, setSelectedDocumentType] = useState<{
|
|
49
|
-
type: GovernmentDocumentType;
|
|
50
|
-
region: string;
|
|
51
|
-
}>({
|
|
52
|
-
type: 'identity_card',
|
|
53
|
-
region: 'root'
|
|
54
|
-
});
|
|
55
|
-
const [showDocumentSelection, setShowDocumentSelection] = useState(
|
|
56
|
-
{
|
|
57
|
-
documentSelection: true,
|
|
58
|
-
regionSelection: false
|
|
59
|
-
}
|
|
60
|
-
);
|
|
61
50
|
const [silentCaptureResult, setSilentCaptureResult] = useState<ISilentCaptureResult>({ success: false, isAnalyzing: false });
|
|
51
|
+
const [showQRModal, setShowQRModal] = useState(false);
|
|
52
|
+
|
|
53
|
+
// Mapping des types de documents backend vers SDK
|
|
54
|
+
const documentTypeMapping: Record<string, GovernmentDocumentType> = {
|
|
55
|
+
'nationalId': 'national_id',
|
|
56
|
+
'passport': 'passport',
|
|
57
|
+
'driversLicense': 'drivers_licence',
|
|
58
|
+
'residencePermit': 'permanent_residence',
|
|
59
|
+
'healthInsuranceCard': 'health_insurance_card',
|
|
60
|
+
};
|
|
62
61
|
// const [imageNaturalSize, setImageNaturalSize] = useState<{ width: number; height: number } | null>(null);
|
|
63
62
|
|
|
64
|
-
const { actions, state, } = useTemplateKYCFlowContext();
|
|
65
|
-
const config = component.config as IDCardConfig;
|
|
63
|
+
const { actions, state, env } = useTemplateKYCFlowContext();
|
|
66
64
|
|
|
67
65
|
|
|
68
66
|
|
|
@@ -74,32 +72,60 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
|
|
|
74
72
|
return "";
|
|
75
73
|
};
|
|
76
74
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
if (
|
|
81
|
-
|
|
82
|
-
return countryMapping;
|
|
75
|
+
// Récupérer les données depuis le composant country_selection
|
|
76
|
+
const countrySelectionData = useMemo(() => {
|
|
77
|
+
const countrySelectionComponent = state.template.components.find(c => c.type === 'country_selection');
|
|
78
|
+
if (countrySelectionComponent) {
|
|
79
|
+
return state.componentData[countrySelectionComponent.id];
|
|
83
80
|
}
|
|
84
81
|
return null;
|
|
85
|
-
}, [state.componentData]);
|
|
82
|
+
}, [state.template.components, state.componentData]);
|
|
86
83
|
|
|
84
|
+
// Extraire selectedDocumentType depuis countrySelectionData
|
|
85
|
+
const selectedDocumentType = useMemo<{ type: GovernmentDocumentType; region: string } | null>(() => {
|
|
86
|
+
if (!countrySelectionData?.documentType) return null;
|
|
87
|
+
|
|
88
|
+
const backendDocType = countrySelectionData.documentType;
|
|
89
|
+
const mappedType = documentTypeMapping[backendDocType] || backendDocType as GovernmentDocumentType;
|
|
90
|
+
const region = countrySelectionData.region || 'root';
|
|
91
|
+
|
|
92
|
+
return { type: mappedType, region };
|
|
93
|
+
}, [countrySelectionData, documentTypeMapping]);
|
|
94
|
+
|
|
95
|
+
// Récupérer countryData pour compatibilité avec le code existant
|
|
96
|
+
const countryData = useMemo(() => {
|
|
97
|
+
return countrySelectionData;
|
|
98
|
+
}, [countrySelectionData]);
|
|
87
99
|
|
|
88
|
-
const availableDocumentTypes = useMemo(() => {
|
|
89
|
-
const newconfig: GovernmentDocumentType[] = [];
|
|
90
100
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
101
|
+
// Synchroniser capturedImages avec value quand les données sont chargées (ex: reprise de session)
|
|
102
|
+
useEffect(() => {
|
|
103
|
+
if (value && Object.keys(value).length > 0) {
|
|
104
|
+
// Vérifier si les données ont changé
|
|
105
|
+
const valueChanged = JSON.stringify(value) !== JSON.stringify(capturedImages);
|
|
106
|
+
if (valueChanged) {
|
|
107
|
+
logger.log("Updating capturedImages from value:", Object.keys(value));
|
|
108
|
+
logger.log("Value data sample:", truncateFields(value));
|
|
109
|
+
const updatedImages = value as Record<string, IIDCardPayload>;
|
|
110
|
+
setCapturedImages(updatedImages);
|
|
111
|
+
|
|
112
|
+
// Si on a des images chargées, mettre à jour silentCaptureResult pour l'affichage
|
|
113
|
+
Object.keys(updatedImages).forEach((side) => {
|
|
114
|
+
const imageData = updatedImages[side];
|
|
115
|
+
if (imageData?.dir) {
|
|
116
|
+
setSilentCaptureResult(prev => ({
|
|
117
|
+
...prev,
|
|
118
|
+
path: imageData.dir,
|
|
119
|
+
success: true,
|
|
120
|
+
isAnalyzing: false,
|
|
121
|
+
mrz: imageData.mrz || '',
|
|
122
|
+
templatePath: imageData.templatePath || '',
|
|
123
|
+
}));
|
|
124
|
+
}
|
|
96
125
|
});
|
|
97
126
|
}
|
|
98
127
|
}
|
|
99
|
-
|
|
100
|
-
return newconfig;
|
|
101
|
-
}, []);
|
|
102
|
-
|
|
128
|
+
}, [value]);
|
|
103
129
|
|
|
104
130
|
useEffect(() => {
|
|
105
131
|
logger.log("cropImageUri", JSON.stringify(truncateFields({ box: silentCaptureResult }), null, 2));
|
|
@@ -112,28 +138,33 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
|
|
|
112
138
|
|
|
113
139
|
|
|
114
140
|
|
|
115
|
-
const cameraConfig = {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
cameraType: 'back' as const,
|
|
120
|
-
allowRetake: true,
|
|
121
|
-
maxRetakes: 3,
|
|
122
|
-
overlay: {
|
|
123
|
-
showGuide: true,
|
|
124
|
-
guideText: selectedDocumentType ? (locale === 'en' ? getDocumentTypeInfo(selectedDocumentType.type).instructions.en : getDocumentTypeInfo(selectedDocumentType.type).instructions.fr) : getLocalizedText(component.instructions as Record<string, LocalizedText>),
|
|
125
|
-
bbox: selectedDocumentType && config.bbox_configs[selectedDocumentType.type] ? config.bbox_configs[selectedDocumentType.type] : {
|
|
126
|
-
xMin: 20,
|
|
127
|
-
yMin: 140,
|
|
128
|
-
xMax: 370,
|
|
129
|
-
yMax: 340,
|
|
130
|
-
borderColor: '#2DBD60',
|
|
131
|
-
borderWidth: 3,
|
|
132
|
-
cornerRadius: 8
|
|
133
|
-
}
|
|
141
|
+
const cameraConfig = useMemo(() => {
|
|
142
|
+
const instructions = selectedDocumentType
|
|
143
|
+
? (locale === 'en' ? getDocumentTypeInfo(selectedDocumentType.type).instructions.en : getDocumentTypeInfo(selectedDocumentType.type).instructions.fr)
|
|
144
|
+
: getLocalizedText(component.instructions as Record<string, LocalizedText>);
|
|
134
145
|
|
|
135
|
-
|
|
136
|
-
|
|
146
|
+
return {
|
|
147
|
+
aspectRatio: 4 / 3,
|
|
148
|
+
quality: 0.8,
|
|
149
|
+
flashMode: 'auto' as const,
|
|
150
|
+
cameraType: 'back' as const,
|
|
151
|
+
allowRetake: true,
|
|
152
|
+
maxRetakes: 3,
|
|
153
|
+
overlay: {
|
|
154
|
+
showGuide: true,
|
|
155
|
+
guideText: instructions,
|
|
156
|
+
bbox: {
|
|
157
|
+
xMin: 20,
|
|
158
|
+
yMin: 140,
|
|
159
|
+
xMax: 370,
|
|
160
|
+
yMax: 340,
|
|
161
|
+
borderColor: '#2DBD60',
|
|
162
|
+
borderWidth: 3,
|
|
163
|
+
cornerRadius: 8
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
}, [selectedDocumentType, locale, component.instructions]);
|
|
137
168
|
|
|
138
169
|
const retakePicture = (currentSide: string) => {
|
|
139
170
|
setShowCamera(true);
|
|
@@ -144,29 +175,18 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
|
|
|
144
175
|
onValueChange({ ...value, [currentSide]: { dir: '', file: '', mrz: '' } });
|
|
145
176
|
};
|
|
146
177
|
|
|
147
|
-
const hasRegions = useCallback((documentType: GovernmentDocumentType): { hasRegions: boolean, regionMapping: string[] } => {
|
|
148
|
-
const regionMapping = countryData?.regionMapping[documentType as GovernmentDocumentType];
|
|
149
|
-
if (regionMapping && Object.keys(regionMapping).length > 1) {
|
|
150
|
-
return {
|
|
151
|
-
hasRegions: true,
|
|
152
|
-
regionMapping: Object.keys(regionMapping)
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
|
-
return {
|
|
156
|
-
hasRegions: false,
|
|
157
|
-
regionMapping: []
|
|
158
|
-
};
|
|
159
|
-
}, [countryData]);
|
|
160
178
|
|
|
161
179
|
|
|
162
180
|
const getCurrentSideVerification = (currentSide: string) => {
|
|
163
|
-
|
|
181
|
+
if (!selectedDocumentType || !countryData?.regionMapping) {
|
|
182
|
+
return { authMethod: [], mrzTypes: [], regionMapping: null, key: 'root' };
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const regionMapping = countryData.regionMapping[selectedDocumentType.type as GovernmentDocumentType];
|
|
164
186
|
const authMethod: string[] = [];
|
|
165
187
|
const mrzTypes: string[] = [];
|
|
166
188
|
|
|
167
|
-
const key = selectedDocumentType
|
|
168
|
-
// console.log("regionMapping", JSON.stringify(regionMapping, null, 2), selectedDocumentType?.region);
|
|
169
|
-
// const key = selectedDocumentType?.region as keyof typeof regionMapping;
|
|
189
|
+
const key = selectedDocumentType.region?.trim()?.length > 0 ? selectedDocumentType.region.trim() : 'root';
|
|
170
190
|
|
|
171
191
|
if (regionMapping?.[key] && Array.isArray(regionMapping[key])) {
|
|
172
192
|
regionMapping[key].forEach((item: any) => {
|
|
@@ -179,13 +199,13 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
|
|
|
179
199
|
});
|
|
180
200
|
}
|
|
181
201
|
|
|
182
|
-
logger.log("regionMapping", JSON.stringify(truncateFields({ regionMapping, selectedDocumentType: selectedDocumentType
|
|
202
|
+
logger.log("regionMapping", JSON.stringify(truncateFields({ regionMapping, selectedDocumentType: selectedDocumentType.region, key }), null, 2));
|
|
183
203
|
|
|
184
204
|
return { authMethod: removeDuplicates(authMethod), mrzTypes: removeDuplicates(mrzTypes), regionMapping: regionMapping, key: key };
|
|
185
205
|
}
|
|
186
206
|
|
|
187
207
|
const getCorrespondingMrzType = (templatePath: string, mapping: any, selectedDocumentType: string = "root") => {
|
|
188
|
-
if (!mapping[selectedDocumentType]) return null;
|
|
208
|
+
if (!mapping || !mapping[selectedDocumentType]) return null;
|
|
189
209
|
|
|
190
210
|
// Extraire le nom du fichier depuis le template_path
|
|
191
211
|
const fileName = templatePath.split("/").pop()?.replace(".jpg", "").replace(".png", "");
|
|
@@ -210,11 +230,21 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
|
|
|
210
230
|
let templatePath = silentCaptureResult.templatePath || '';
|
|
211
231
|
let templateBbox: IBbox | undefined;
|
|
212
232
|
|
|
233
|
+
if (!selectedDocumentType) {
|
|
234
|
+
setSilentCaptureResult((prev) => ({ ...prev, isAnalyzing: false, success: false, error: 'Document type not selected' }));
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
|
|
213
238
|
const regionMappings = getCurrentSideVerification(currentSide)
|
|
214
239
|
// logger.log("regionMappings", JSON.stringify(truncateFields({regionMappings, templatePath}), null, 2));
|
|
215
240
|
if (templatePath.length === 0) {
|
|
216
241
|
try {
|
|
217
|
-
|
|
242
|
+
logger.log("checkTemplateType - BEFORE call", {
|
|
243
|
+
selectedDocumentTypeType: selectedDocumentType?.type,
|
|
244
|
+
countrySelectionDataDocumentType: countrySelectionData?.documentType,
|
|
245
|
+
docTypeToSend: selectedDocumentType?.type
|
|
246
|
+
});
|
|
247
|
+
const templateType = await checkTemplateType({ path: result.path || '', docType: selectedDocumentType?.type as GovernmentDocumentType, docRegion: countryData?.code || "", postfix: currentSide }, env);
|
|
218
248
|
|
|
219
249
|
if (templateType.template_path) {
|
|
220
250
|
templatePath = templateType.template_path;
|
|
@@ -232,21 +262,38 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
|
|
|
232
262
|
} catch (e: any) {
|
|
233
263
|
logger.log("error checking template type", truncateFields(e));
|
|
234
264
|
setSilentCaptureResult((prev) => ({ ...prev, isAnalyzing: false, success: false, error: e?.message || 'Erreur de vérification du template' }));
|
|
265
|
+
return; // Return early if checkTemplateType fails
|
|
235
266
|
}
|
|
236
267
|
}
|
|
237
|
-
logger.log("templatePath before
|
|
268
|
+
logger.log("templatePath before verification", templatePath, "currentSide", currentSide);
|
|
238
269
|
if (currentSide === 'front') {
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
270
|
+
logger.log("frontVerification - BEFORE call", {
|
|
271
|
+
selectedDocumentTypeType: selectedDocumentType?.type,
|
|
272
|
+
countrySelectionDataDocumentType: countrySelectionData?.documentType,
|
|
273
|
+
docTypeToSend: selectedDocumentType?.type
|
|
274
|
+
});
|
|
275
|
+
logger.log("Calling frontVerification", { templatePath, selectedDocumentType: selectedDocumentType?.type, regionMappings });
|
|
276
|
+
console.log("About to call frontVerification", typeof frontVerification);
|
|
277
|
+
try {
|
|
278
|
+
const mrzType = getCorrespondingMrzType(templatePath, regionMappings.regionMapping, regionMappings.key || '') || '';
|
|
279
|
+
console.log("mrzType calculated", mrzType);
|
|
280
|
+
const verificationParams = {
|
|
242
281
|
path: result.path,
|
|
243
|
-
regionMapping:
|
|
244
|
-
|
|
282
|
+
regionMapping: {
|
|
283
|
+
authMethod: regionMappings.authMethod,
|
|
284
|
+
mrzTypes: regionMappings.mrzTypes,
|
|
285
|
+
},
|
|
286
|
+
selectedDocumentType: GovernmentDocumentTypeShorted[selectedDocumentType?.type as keyof typeof GovernmentDocumentTypeShorted] || '',
|
|
245
287
|
code: countryData?.code || '',
|
|
246
288
|
currentSide: currentSide,
|
|
247
289
|
templatePath: templatePath,
|
|
248
|
-
mrzType:
|
|
249
|
-
}
|
|
290
|
+
mrzType: mrzType,
|
|
291
|
+
};
|
|
292
|
+
console.log("frontVerification params", verificationParams);
|
|
293
|
+
console.log("About to call frontVerification function");
|
|
294
|
+
const promise = frontVerification(verificationParams, env);
|
|
295
|
+
console.log("frontVerification promise created", promise);
|
|
296
|
+
promise.then((mrz) => {
|
|
250
297
|
logger.log("front verification result", truncateFields(mrz));
|
|
251
298
|
const bbox = (mrz as any)?.bbox || templateBbox;
|
|
252
299
|
setSilentCaptureResult((prev) => ({
|
|
@@ -256,7 +303,7 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
|
|
|
256
303
|
bbox: bbox, success: true,
|
|
257
304
|
mrz: JSON.stringify(mrz), isAnalyzing: false,
|
|
258
305
|
country: countryData?.code,
|
|
259
|
-
documentType: selectedDocumentType
|
|
306
|
+
documentType: selectedDocumentType.type,
|
|
260
307
|
}),
|
|
261
308
|
);
|
|
262
309
|
// Stocker le bbox pour ce côté
|
|
@@ -265,27 +312,37 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
|
|
|
265
312
|
}
|
|
266
313
|
|
|
267
314
|
}).catch((e: any) => {
|
|
315
|
+
console.log("error front verification", e);
|
|
268
316
|
logger.log("error front verification", truncateFields(e));
|
|
269
317
|
setSilentCaptureResult((prev) => ({ ...prev, isAnalyzing: false, templatePath: templatePath, success: false, error: e?.message || 'Erreur de détection du MRZ' }));
|
|
270
|
-
//
|
|
271
|
-
})
|
|
318
|
+
// showAlert('Erreur', e?.message || 'Erreur de détection du MRZ');
|
|
319
|
+
});
|
|
320
|
+
} catch (error: any) {
|
|
321
|
+
console.log("Error setting up frontVerification call", error);
|
|
322
|
+
setSilentCaptureResult((prev) => ({ ...prev, isAnalyzing: false, success: false, error: error?.message || 'Erreur lors de la configuration de la vérification' }));
|
|
323
|
+
}
|
|
272
324
|
} else {
|
|
325
|
+
const backRegionMappings = getCurrentSideVerification(currentSide);
|
|
326
|
+
logger.log("Calling backVerification", { templatePath, selectedDocumentType: selectedDocumentType.type, backRegionMappings });
|
|
273
327
|
backVerification({
|
|
274
328
|
path: result.path,
|
|
275
|
-
regionMapping:
|
|
276
|
-
|
|
329
|
+
regionMapping: {
|
|
330
|
+
authMethod: backRegionMappings.authMethod,
|
|
331
|
+
mrzTypes: backRegionMappings.mrzTypes,
|
|
332
|
+
},
|
|
333
|
+
selectedDocumentType: GovernmentDocumentTypeShorted[selectedDocumentType.type as keyof typeof GovernmentDocumentTypeShorted] || '',
|
|
277
334
|
code: countryData?.code || '',
|
|
278
335
|
currentSide: currentSide,
|
|
279
336
|
templatePath: templatePath,
|
|
280
|
-
mrzType: getCorrespondingMrzType(templatePath,
|
|
281
|
-
}).then((mrz) => {
|
|
337
|
+
mrzType: getCorrespondingMrzType(templatePath, backRegionMappings.regionMapping, backRegionMappings.key || '') || '',
|
|
338
|
+
}, env).then((mrz) => {
|
|
282
339
|
logger.log("back verification result", truncateFields(mrz));
|
|
283
340
|
const bbox = (mrz as any)?.bbox || templateBbox;
|
|
284
341
|
setSilentCaptureResult((prev) => ({
|
|
285
342
|
...prev, path: result.path, bbox: bbox,
|
|
286
343
|
success: true, mrz: JSON.stringify(mrz), isAnalyzing: false,
|
|
287
344
|
country: countryData?.code,
|
|
288
|
-
documentType: selectedDocumentType
|
|
345
|
+
documentType: selectedDocumentType.type,
|
|
289
346
|
|
|
290
347
|
}));
|
|
291
348
|
// Stocker le bbox pour ce côté
|
|
@@ -295,7 +352,7 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
|
|
|
295
352
|
}).catch((e: any) => {
|
|
296
353
|
logger.log("error back verification", truncateFields(e));
|
|
297
354
|
setSilentCaptureResult((prev) => ({ ...prev, isAnalyzing: false, templatePath: templatePath, success: false, error: e?.message || 'Erreur de détection du MRZ' }));
|
|
298
|
-
//
|
|
355
|
+
// showAlert('Erreur', e?.message || 'Erreur de détection du MRZ');
|
|
299
356
|
})
|
|
300
357
|
}
|
|
301
358
|
|
|
@@ -304,28 +361,45 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
|
|
|
304
361
|
|
|
305
362
|
// Handle capture
|
|
306
363
|
const handleCapture = async (result: { success: boolean; path?: string; error?: string }) => {
|
|
364
|
+
console.log("handleCapture", JSON.stringify(truncateFields({ result, silentCaptureResult }), null, 2));
|
|
365
|
+
|
|
307
366
|
if (silentCaptureResult.path) {
|
|
308
|
-
|
|
367
|
+
// Créer une image rognée avec tolérance de 10% pour l'envoi
|
|
368
|
+
let imagePathForUpload = silentCaptureResult.path;
|
|
369
|
+
if (silentCaptureResult.bbox) {
|
|
370
|
+
try {
|
|
371
|
+
logger.log("Début du rognage avec tolérance, URI original:", silentCaptureResult.path);
|
|
372
|
+
imagePathForUpload = await cropImageWithBBoxWithTolerance(silentCaptureResult.path, silentCaptureResult.bbox, 0.05);
|
|
373
|
+
logger.log("Image rognée avec tolérance créée pour l'envoi, URI final:", imagePathForUpload);
|
|
374
|
+
} catch (error) {
|
|
375
|
+
logger.log("Erreur lors du rognage avec tolérance, utilisation de l'image originale:", truncateFields(error));
|
|
376
|
+
// En cas d'erreur, on utilise l'image originale
|
|
377
|
+
imagePathForUpload = silentCaptureResult.path;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
const base64 = await pathToBase64(imagePathForUpload);
|
|
309
382
|
|
|
310
383
|
logger.log("silentCaptureResult captured", JSON.stringify(truncateFields(silentCaptureResult), null, 2));
|
|
384
|
+
// Utiliser l'image originale pour l'affichage (dir) mais l'image rognée avec tolérance pour l'envoi (file)
|
|
311
385
|
const newImages = { ...capturedImages, [currentSide]: { dir: silentCaptureResult.path, file: base64, mrz: silentCaptureResult.mrz || "", templatePath: silentCaptureResult.templatePath } };
|
|
312
386
|
setCapturedImages(newImages);
|
|
313
387
|
if (silentCaptureResult.country && silentCaptureResult.documentType) {
|
|
314
388
|
onValueChange({
|
|
315
389
|
...newImages,
|
|
316
390
|
country: silentCaptureResult.country,
|
|
317
|
-
documentType: silentCaptureResult.documentType,
|
|
391
|
+
documentType: GovernmentDocumentTypeBackend[silentCaptureResult.documentType as keyof typeof GovernmentDocumentTypeBackend] || '',
|
|
318
392
|
});
|
|
319
393
|
}
|
|
320
394
|
setShowCamera(false);
|
|
321
395
|
actions.showCustomStepper(true);
|
|
322
396
|
} else {
|
|
323
|
-
|
|
397
|
+
showAlert('Erreur', result.error || 'Impossible de prendre la photo');
|
|
324
398
|
}
|
|
325
399
|
};
|
|
326
400
|
|
|
327
401
|
const handleError = (event: { message: string }) => {
|
|
328
|
-
|
|
402
|
+
showAlert('Erreur', event.message);
|
|
329
403
|
setShowCamera(false);
|
|
330
404
|
};
|
|
331
405
|
|
|
@@ -337,98 +411,75 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
|
|
|
337
411
|
actions.showCustomStepper(!showCamera);
|
|
338
412
|
}, [showCamera]);
|
|
339
413
|
|
|
414
|
+
// Cross-device polling logic
|
|
415
|
+
useEffect(() => {
|
|
416
|
+
if (!showQRModal || !state.session.session_id) return;
|
|
417
|
+
|
|
418
|
+
const pollInterval = setInterval(async () => {
|
|
419
|
+
try {
|
|
420
|
+
const result = await kycService.getVerificationResult(state.session.session_id);
|
|
421
|
+
const sessionData = result[state.session.session_id]?.data;
|
|
422
|
+
|
|
423
|
+
if (sessionData) {
|
|
424
|
+
// Check if verification is completed or if we have ID card data
|
|
425
|
+
// Since the requirement is "verification restarts", we might look for overall completion
|
|
426
|
+
// or we could check if user_data is populated.
|
|
427
|
+
// For now, let's assume if status is not PENDING/INITIALIZED it might be done.
|
|
428
|
+
// Or simplier: if the mobile flow completes, the session status updates.
|
|
429
|
+
logger.log('Polling result:', truncateFields(sessionData));
|
|
430
|
+
|
|
431
|
+
if (sessionData.verification_status === 'completed' || sessionData.verification_status === 'approved' || sessionData.verification_status === 'review') {
|
|
432
|
+
clearInterval(pollInterval);
|
|
433
|
+
setShowQRModal(false);
|
|
434
|
+
actions.submitVerification(); // Or handleComplete
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
} catch (error) {
|
|
438
|
+
console.error('Polling error:', error);
|
|
439
|
+
}
|
|
440
|
+
}, 5000);
|
|
441
|
+
|
|
442
|
+
return () => clearInterval(pollInterval);
|
|
443
|
+
}, [showQRModal, state.session.session_id, actions]);
|
|
444
|
+
|
|
445
|
+
const getQrCodeUrl = (): string => {
|
|
446
|
+
// Only available on web platform
|
|
447
|
+
if (Platform.OS !== 'web') return '';
|
|
448
|
+
if (typeof window === 'undefined' || !window.location || !window.location.href) return '';
|
|
449
|
+
|
|
450
|
+
try {
|
|
451
|
+
const currentUrl = new URL(window.location.href);
|
|
452
|
+
if (!currentUrl.searchParams.has('kyc_id') && state.session.session_id) {
|
|
453
|
+
currentUrl.searchParams.set('kyc_id', state.session.session_id);
|
|
454
|
+
}
|
|
455
|
+
// Ajouter l'étape actuelle pour permettre à l'utilisateur de continuer au bon endroit
|
|
456
|
+
if (!currentUrl.searchParams.has('step')) {
|
|
457
|
+
currentUrl.searchParams.set('step', String(state.currentComponentIndex));
|
|
458
|
+
}
|
|
459
|
+
return `https://api.qrserver.com/v1/create-qr-code/?size=250x250&data=${encodeURIComponent(currentUrl.toString())}`;
|
|
460
|
+
} catch (error) {
|
|
461
|
+
console.warn('Error generating QR code URL:', error);
|
|
462
|
+
return '';
|
|
463
|
+
}
|
|
464
|
+
};
|
|
465
|
+
|
|
340
466
|
|
|
341
467
|
|
|
342
468
|
|
|
343
469
|
|
|
344
470
|
|
|
345
|
-
const handleDocumentTypeSelection = (docType: GovernmentDocumentType) => {
|
|
346
|
-
setSelectedDocumentType({ type: docType, region: hasRegions(docType) ? '' : 'root' });
|
|
347
|
-
setSilentCaptureResult((prev) => ({ ...prev, templatePath: '', bbox: undefined, success: false, isAnalyzing: false, error: '', path: '', mrz: '', country: '', documentType: '' }));
|
|
348
|
-
setCapturedImages((prev) => ({ front: { dir: '', file: '', mrz: '', templatePath: '' }, back: { dir: '', file: '', mrz: '', templatePath: '' } }));
|
|
349
|
-
};
|
|
350
|
-
const handleRegionSelection = (region: string) => {
|
|
351
|
-
setSelectedDocumentType((prev) => ({ ...prev, region: region }));
|
|
352
|
-
};
|
|
353
471
|
|
|
354
|
-
//
|
|
355
|
-
if (
|
|
472
|
+
// Vérifier si les données sont disponibles, sinon afficher un message d'erreur
|
|
473
|
+
if (!countrySelectionData || !selectedDocumentType) {
|
|
356
474
|
return (
|
|
357
475
|
<View style={styles.root}>
|
|
358
476
|
<View style={styles.container}>
|
|
359
|
-
|
|
360
477
|
<Text style={styles.title}>{getLocalizedText(component.labels)}</Text>
|
|
361
|
-
<Text style={styles.description}>
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
return (
|
|
367
|
-
<TouchableOpacity
|
|
368
|
-
key={docType}
|
|
369
|
-
style={[styles.documentTypeButton, { backgroundColor: selectedDocumentType?.type === docType ? '#2DBD6030' : '#F8F9FA', borderColor: selectedDocumentType?.type === docType ? '#2DBD60' : '#E9ECEF', borderWidth: 2 }]}
|
|
370
|
-
onPress={() => handleDocumentTypeSelection(docType)}
|
|
371
|
-
>
|
|
372
|
-
<View style={[styles.documentTypeIconContainer, { backgroundColor: 'gray', }]}>
|
|
373
|
-
<Text style={styles.documentTypeIcon}>{docInfo.icon}</Text>
|
|
374
|
-
</View>
|
|
375
|
-
<Text style={styles.documentTypeName}>
|
|
376
|
-
{docInfo.name[language as keyof typeof docInfo.name] || docInfo.name.en}
|
|
377
|
-
</Text>
|
|
378
|
-
<Text style={styles.documentTypeArrow}>→</Text>
|
|
379
|
-
</TouchableOpacity>
|
|
380
|
-
);
|
|
381
|
-
}) : null}
|
|
382
|
-
{showDocumentSelection.regionSelection ?
|
|
383
|
-
hasRegions(selectedDocumentType?.type || 'identity_card').regionMapping.map((region) => {
|
|
384
|
-
return (
|
|
385
|
-
<TouchableOpacity key={region} onPress={() => handleRegionSelection(region)}
|
|
386
|
-
style={[styles.documentTypeButton, { backgroundColor: selectedDocumentType?.region === region ? '#2DBD6030' : '#F8F9FA', borderColor: selectedDocumentType?.region === region ? '#2DBD60' : '#E9ECEF', borderWidth: 2 }]}
|
|
387
|
-
|
|
388
|
-
>
|
|
389
|
-
<Text>{region}</Text>
|
|
390
|
-
</TouchableOpacity>
|
|
391
|
-
);
|
|
392
|
-
}) : null}
|
|
393
|
-
</ScrollView>
|
|
394
|
-
<Button title={t('common.next')} onPress={() => {
|
|
395
|
-
if (!selectedDocumentType) {
|
|
396
|
-
Alert.alert(t('common.error'), t('validation.required'));
|
|
397
|
-
return;
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
if (showDocumentSelection.regionSelection) {
|
|
401
|
-
logger.log("showDocumentSelection", JSON.stringify(truncateFields(showDocumentSelection), null, 2));
|
|
402
|
-
setShowDocumentSelection({
|
|
403
|
-
documentSelection: false,
|
|
404
|
-
regionSelection: false
|
|
405
|
-
});
|
|
406
|
-
setShowCamera(true);
|
|
407
|
-
actions.showCustomStepper(false);
|
|
408
|
-
return;
|
|
409
|
-
} else {
|
|
410
|
-
if (hasRegions(selectedDocumentType?.type)?.hasRegions) {
|
|
411
|
-
setShowDocumentSelection({
|
|
412
|
-
documentSelection: false,
|
|
413
|
-
regionSelection: true
|
|
414
|
-
});
|
|
415
|
-
} else {
|
|
416
|
-
setShowDocumentSelection({
|
|
417
|
-
documentSelection: false,
|
|
418
|
-
regionSelection: false
|
|
419
|
-
});
|
|
420
|
-
setShowCamera(true);
|
|
421
|
-
actions.showCustomStepper(false);
|
|
422
|
-
}
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
}}
|
|
426
|
-
variant={selectedDocumentType ? 'primary' : "neutral"}
|
|
427
|
-
size="large"
|
|
428
|
-
fullWidth
|
|
429
|
-
/>
|
|
430
|
-
|
|
431
|
-
|
|
478
|
+
<Text style={styles.description}>
|
|
479
|
+
{state.currentLanguage === "en"
|
|
480
|
+
? "Please complete the country and document selection first."
|
|
481
|
+
: "Veuillez d'abord compléter la sélection du pays et du document."}
|
|
482
|
+
</Text>
|
|
432
483
|
{error && (
|
|
433
484
|
<Text style={styles.errorText}>{error}</Text>
|
|
434
485
|
)}
|
|
@@ -470,10 +521,6 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
|
|
|
470
521
|
if (currentSide === 'back') {
|
|
471
522
|
setCurrentSide('front');
|
|
472
523
|
setShowCamera(false);
|
|
473
|
-
setShowDocumentSelection({
|
|
474
|
-
documentSelection: false,
|
|
475
|
-
regionSelection: false
|
|
476
|
-
});
|
|
477
524
|
// Si une image front existe, on la restaure pour l'afficher
|
|
478
525
|
if (capturedImages['front']?.dir) {
|
|
479
526
|
// Restaurer l'état de capture du front pour afficher l'image
|
|
@@ -496,14 +543,11 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
|
|
|
496
543
|
setSilentCaptureResult((prev) => ({ path: '', success: false, isAnalyzing: false, error: '', templatePath: '' }));
|
|
497
544
|
}
|
|
498
545
|
} else {
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
regionSelection: false
|
|
502
|
-
});
|
|
503
|
-
actions.showCustomStepper(true);
|
|
546
|
+
// Retour au composant précédent (country_selection)
|
|
547
|
+
actions.previousComponent();
|
|
504
548
|
}
|
|
505
549
|
},
|
|
506
|
-
selectedDocumentType: locale === 'en' ? getDocumentTypeInfo(selectedDocumentType
|
|
550
|
+
selectedDocumentType: selectedDocumentType ? (locale === 'en' ? getDocumentTypeInfo(selectedDocumentType.type).name.en : getDocumentTypeInfo(selectedDocumentType.type).name.fr) : '',
|
|
507
551
|
step: state.currentComponentIndex + 1,
|
|
508
552
|
totalSteps: state.template.components.length,
|
|
509
553
|
side: currentSide,
|
|
@@ -557,41 +601,74 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
|
|
|
557
601
|
}}
|
|
558
602
|
/>
|
|
559
603
|
) : null}
|
|
560
|
-
{silentCaptureResult.path ? (
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
604
|
+
{!cropImageUri && silentCaptureResult.path ? (
|
|
605
|
+
<Image
|
|
606
|
+
source={{ uri: silentCaptureResult.path }}
|
|
607
|
+
style={{
|
|
608
|
+
width: '100%',
|
|
609
|
+
height: 200,
|
|
610
|
+
borderRadius: 12,
|
|
611
|
+
resizeMode: 'cover',
|
|
612
|
+
}}
|
|
613
|
+
/>
|
|
614
|
+
) : null}
|
|
615
|
+
{!cropImageUri && !silentCaptureResult.path && capturedImages[currentSide]?.dir ? (
|
|
616
|
+
<Image
|
|
617
|
+
source={{ uri: capturedImages[currentSide].dir }}
|
|
618
|
+
style={{
|
|
619
|
+
width: '100%',
|
|
620
|
+
height: 200,
|
|
621
|
+
borderRadius: 12,
|
|
622
|
+
resizeMode: 'cover',
|
|
623
|
+
}}
|
|
624
|
+
/>
|
|
625
|
+
) : null}
|
|
569
626
|
|
|
570
627
|
</View>
|
|
571
|
-
{/*
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
628
|
+
{/* Capture button si aucune image n'a été capturée */}
|
|
629
|
+
{!capturedImages[currentSide]?.dir && (
|
|
630
|
+
<Button
|
|
631
|
+
title={state.currentLanguage === "en" ? "Take Photo" : "Prendre une photo"}
|
|
632
|
+
onPress={() => {
|
|
633
|
+
setShowCamera(true);
|
|
634
|
+
actions.showCustomStepper(false);
|
|
635
|
+
}}
|
|
636
|
+
variant="primary"
|
|
637
|
+
size="large"
|
|
638
|
+
fullWidth
|
|
639
|
+
/>
|
|
640
|
+
)}
|
|
641
|
+
{/* retake button si une image a été capturée */}
|
|
642
|
+
{capturedImages[currentSide]?.dir && (
|
|
643
|
+
<>
|
|
644
|
+
<Button title={t('kyc.idCardCapture.retakeButton')} onPress={() => {
|
|
645
|
+
retakePicture(currentSide);
|
|
646
|
+
}}
|
|
647
|
+
variant="outline"
|
|
648
|
+
size="medium"
|
|
649
|
+
fullWidth
|
|
650
|
+
/>
|
|
651
|
+
<Button title={t('common.next')} onPress={() => {
|
|
652
|
+
if (!selectedDocumentType) {
|
|
653
|
+
showAlert('Error', 'Document type not selected');
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
if (currentSide === 'back' || selectedDocumentType.type === 'passport') {
|
|
657
|
+
actions.nextComponent();
|
|
658
|
+
return;
|
|
659
|
+
} else {
|
|
660
|
+
setShowCamera(true);
|
|
661
|
+
setCurrentSide('back');
|
|
662
|
+
setSilentCaptureResult((prev) => ({ path: '', success: false, isAnalyzing: false, error: '', mrz: '', templatePath: '' }));
|
|
663
|
+
setCropImageUri('');
|
|
664
|
+
}
|
|
665
|
+
}}
|
|
666
|
+
variant="primary"
|
|
667
|
+
size="large"
|
|
668
|
+
fullWidth
|
|
669
|
+
/>
|
|
670
|
+
</>
|
|
671
|
+
)}
|
|
595
672
|
</View>
|
|
596
673
|
</View>
|
|
597
674
|
</ScrollView>
|
|
@@ -601,6 +678,49 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({
|
|
|
601
678
|
{error && (
|
|
602
679
|
<Text style={styles.errorText}>{error}</Text>
|
|
603
680
|
)}
|
|
681
|
+
|
|
682
|
+
{/* Cross-Device / Continue on Phone Button (Web Only) */}
|
|
683
|
+
{Platform.OS === 'web' && !isMobileWeb() && !capturedImages[currentSide]?.dir && (
|
|
684
|
+
<Button title={t('kyc.idCardCapture.continueOnPhone')} onPress={() => { setShowQRModal(true) }} />
|
|
685
|
+
)}
|
|
686
|
+
|
|
687
|
+
{/* QR Code Modal - Web Only */}
|
|
688
|
+
{Platform.OS === 'web' && (
|
|
689
|
+
<Modal
|
|
690
|
+
visible={showQRModal}
|
|
691
|
+
transparent={true}
|
|
692
|
+
animationType="fade"
|
|
693
|
+
onRequestClose={() => setShowQRModal(false)}
|
|
694
|
+
>
|
|
695
|
+
<View style={styles.modalOverlay}>
|
|
696
|
+
<View style={styles.modalContent}>
|
|
697
|
+
<Text style={styles.modalTitle}>
|
|
698
|
+
{t('kyc.idCardCapture.continueOnMobile')}
|
|
699
|
+
</Text>
|
|
700
|
+
<Text style={styles.modalDescription}>
|
|
701
|
+
{t('kyc.idCardCapture.scanQrCode')}
|
|
702
|
+
</Text>
|
|
703
|
+
|
|
704
|
+
{showQRModal && getQrCodeUrl() ? (
|
|
705
|
+
<Image
|
|
706
|
+
source={{ uri: getQrCodeUrl() }}
|
|
707
|
+
style={styles.qrCodeImage}
|
|
708
|
+
/>
|
|
709
|
+
) : null}
|
|
710
|
+
|
|
711
|
+
<TouchableOpacity
|
|
712
|
+
style={styles.closeButton}
|
|
713
|
+
onPress={() => setShowQRModal(false)}
|
|
714
|
+
>
|
|
715
|
+
<Text style={styles.closeButtonText}>
|
|
716
|
+
{t('common.close')}
|
|
717
|
+
</Text>
|
|
718
|
+
</TouchableOpacity>
|
|
719
|
+
</View>
|
|
720
|
+
</View>
|
|
721
|
+
</Modal>
|
|
722
|
+
)}
|
|
723
|
+
|
|
604
724
|
</View>
|
|
605
725
|
</View>
|
|
606
726
|
);
|
|
@@ -757,43 +877,66 @@ const styles = StyleSheet.create({
|
|
|
757
877
|
marginTop: 8,
|
|
758
878
|
textAlign: 'center',
|
|
759
879
|
},
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
// paddingHorizontal: 16,
|
|
764
|
-
maxHeight: Dimensions.get('window').height - 400,
|
|
765
|
-
},
|
|
766
|
-
documentTypeButton: {
|
|
767
|
-
flexDirection: 'row',
|
|
880
|
+
crossDeviceButton: {
|
|
881
|
+
marginTop: 16,
|
|
882
|
+
padding: 12,
|
|
768
883
|
alignItems: 'center',
|
|
769
|
-
backgroundColor: '#F8F9FA',
|
|
770
|
-
padding: 16,
|
|
771
|
-
marginBottom: 12,
|
|
772
|
-
borderRadius: 12,
|
|
773
884
|
borderWidth: 1,
|
|
774
|
-
borderColor: '#
|
|
885
|
+
borderColor: '#2DBD60',
|
|
886
|
+
borderRadius: 8,
|
|
887
|
+
backgroundColor: '#f0f9f0',
|
|
775
888
|
},
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
889
|
+
crossDeviceText: {
|
|
890
|
+
color: '#2DBD60',
|
|
891
|
+
fontSize: 16,
|
|
892
|
+
fontWeight: '600',
|
|
893
|
+
},
|
|
894
|
+
modalOverlay: {
|
|
895
|
+
flex: 1,
|
|
896
|
+
backgroundColor: 'rgba(0,0,0,0.5)',
|
|
781
897
|
justifyContent: 'center',
|
|
782
|
-
|
|
898
|
+
alignItems: 'center',
|
|
783
899
|
},
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
900
|
+
modalContent: {
|
|
901
|
+
backgroundColor: 'white',
|
|
902
|
+
borderRadius: 16,
|
|
903
|
+
padding: 24,
|
|
904
|
+
alignItems: 'center',
|
|
905
|
+
width: '90%',
|
|
906
|
+
maxWidth: 340,
|
|
907
|
+
shadowColor: '#000',
|
|
908
|
+
shadowOffset: { width: 0, height: 2 },
|
|
909
|
+
shadowOpacity: 0.25,
|
|
910
|
+
shadowRadius: 4,
|
|
911
|
+
elevation: 5,
|
|
787
912
|
},
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
913
|
+
modalTitle: {
|
|
914
|
+
fontSize: 20,
|
|
915
|
+
fontWeight: 'bold',
|
|
916
|
+
marginBottom: 12,
|
|
792
917
|
color: '#333',
|
|
793
918
|
},
|
|
794
|
-
|
|
795
|
-
fontSize:
|
|
919
|
+
modalDescription: {
|
|
920
|
+
fontSize: 16,
|
|
796
921
|
color: '#666',
|
|
797
|
-
|
|
922
|
+
textAlign: 'center',
|
|
923
|
+
marginBottom: 20,
|
|
924
|
+
lineHeight: 22,
|
|
925
|
+
},
|
|
926
|
+
qrCodeImage: {
|
|
927
|
+
width: 200,
|
|
928
|
+
height: 200,
|
|
929
|
+
marginBottom: 20,
|
|
930
|
+
},
|
|
931
|
+
closeButton: {
|
|
932
|
+
paddingVertical: 10,
|
|
933
|
+
paddingHorizontal: 20,
|
|
934
|
+
backgroundColor: '#f5f5f5',
|
|
935
|
+
borderRadius: 8,
|
|
936
|
+
},
|
|
937
|
+
closeButtonText: {
|
|
938
|
+
fontSize: 16,
|
|
939
|
+
color: '#666',
|
|
940
|
+
fontWeight: '600',
|
|
798
941
|
},
|
|
799
942
|
});
|