@transfergratis/react-native-sdk 0.1.22 → 0.1.24
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/src/main/AndroidManifest.xml +9 -4
- 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/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/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 +193 -237
- 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.js +3 -2
- package/build/components/KYCElements/OrientationVideoCapture.js.map +1 -1
- package/build/components/KYCElements/OrientationVideoCaptureEnhanced.js +3 -2
- package/build/components/KYCElements/OrientationVideoCaptureEnhanced.js.map +1 -1
- package/build/components/KYCElements/OrientationVideoCaptureFinal.js +3 -2
- package/build/components/KYCElements/OrientationVideoCaptureFinal.js.map +1 -1
- 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 +182 -39
- 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 +4 -2
- package/build/components/TemplateKYCExample.d.ts.map +1 -1
- package/build/components/TemplateKYCExample.js +5 -68
- package/build/components/TemplateKYCExample.js.map +1 -1
- package/build/components/TemplateKYCFlowRefactored.d.ts +2 -1
- package/build/components/TemplateKYCFlowRefactored.d.ts.map +1 -1
- package/build/components/TemplateKYCFlowRefactored.js +95 -9
- 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/allowedDomains.d.ts +30 -0
- package/build/config/allowedDomains.d.ts.map +1 -0
- package/build/config/allowedDomains.js +127 -0
- package/build/config/allowedDomains.js.map +1 -0
- package/build/hooks/useTemplateKYCFlow.d.ts.map +1 -1
- package/build/hooks/useTemplateKYCFlow.js +68 -43
- 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 +9 -0
- package/build/i18n/en/index.d.ts.map +1 -1
- package/build/i18n/en/index.js +9 -0
- package/build/i18n/en/index.js.map +1 -1
- package/build/i18n/fr/index.d.ts +9 -0
- package/build/i18n/fr/index.d.ts.map +1 -1
- package/build/i18n/fr/index.js +9 -0
- package/build/i18n/fr/index.js.map +1 -1
- package/build/index.d.ts +5 -0
- package/build/index.d.ts.map +1 -1
- package/build/index.js +8 -0
- package/build/index.js.map +1 -1
- package/build/modules/api/CardAuthentification.js +1 -0
- package/build/modules/api/CardAuthentification.js.map +1 -1
- package/build/modules/api/KYCService.d.ts +4 -1
- package/build/modules/api/KYCService.d.ts.map +1 -1
- package/build/modules/api/KYCService.js +17 -5
- package/build/modules/api/KYCService.js.map +1 -1
- package/build/modules/api/TemplateService.d.ts +45 -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/modules/api/types.d.ts +1 -0
- package/build/modules/api/types.d.ts.map +1 -1
- package/build/modules/api/types.js.map +1 -1
- package/build/types/KYC.types.d.ts +144 -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/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/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 +353 -0
- package/build/utils/template-transformer.js.map +1 -0
- package/build/web/WebKYCEntry.d.ts.map +1 -1
- package/build/web/WebKYCEntry.js +102 -20
- package/build/web/WebKYCEntry.js.map +1 -1
- package/package.json +1 -1
- package/src/components/EnhancedCameraView.tsx +31 -2
- package/src/components/EnhancedCameraView.web.tsx +24 -0
- package/src/components/KYCElements/CameraCapture.tsx +4 -3
- package/src/components/KYCElements/CountrySelectionTemplate.tsx +410 -113
- package/src/components/KYCElements/FileUpload.tsx +5 -4
- package/src/components/KYCElements/FileUploadTemplate.tsx +5 -4
- package/src/components/KYCElements/IDCardCapture.tsx +196 -254
- package/src/components/KYCElements/LocationCaptureTemplate.tsx +95 -44
- package/src/components/KYCElements/OrientationVideoCapture.tsx +2 -2
- package/src/components/KYCElements/OrientationVideoCaptureEnhanced.tsx +2 -2
- package/src/components/KYCElements/OrientationVideoCaptureFinal.tsx +2 -2
- package/src/components/KYCElements/SelfieCapture.tsx +4 -3
- package/src/components/KYCElements/SelfieCaptureTemplate.tsx +195 -41
- package/src/components/KYCElements/WelcomeTemplate.tsx +289 -0
- package/src/components/TemplateKYCExample.tsx +16 -71
- package/src/components/TemplateKYCFlowRefactored.tsx +121 -11
- package/src/components/example/DynamicTemplateExample.tsx +289 -0
- package/src/config/allowedDomains.ts +152 -0
- package/src/hooks/useTemplateKYCFlow.tsx +71 -46
- package/src/hooks/useTemplateLoader.ts +102 -0
- package/src/i18n/en/index.ts +10 -0
- package/src/i18n/fr/index.ts +9 -0
- package/src/index.ts +11 -0
- package/src/modules/api/CardAuthentification.ts +1 -1
- package/src/modules/api/KYCService.ts +18 -8
- package/src/modules/api/TemplateService.ts +167 -0
- package/src/modules/api/types.ts +1 -0
- package/src/types/KYC.types.ts +188 -3
- package/src/utils/cropByObb.ts +83 -3
- package/src/utils/platformAlert.ts +85 -0
- package/src/utils/template-transformer.ts +433 -0
- package/src/web/WebKYCEntry.tsx +122 -24
|
@@ -1,20 +1,29 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, { useState, useMemo, useEffect } from 'react';
|
|
2
2
|
import { View, Text, TouchableOpacity, StyleSheet, ScrollView } from 'react-native';
|
|
3
|
-
import { TemplateComponent, CountrySelectionConfig, LocalizedText, Country } from '../../types/KYC.types';
|
|
3
|
+
import { TemplateComponent, CountrySelectionConfig, LocalizedText, Country, IDCardConfig } from '../../types/KYC.types';
|
|
4
4
|
import { countryMapping } from '../../config/region_mapping';
|
|
5
5
|
import { useTemplateKYCFlowContext } from '../../hooks/useTemplateKYCFlow';
|
|
6
|
-
import { useI18n } from '../../hooks/useI18n';
|
|
7
6
|
import { Button } from '../ui/Button';
|
|
8
7
|
import { countryData } from '../../config/countriesData';
|
|
9
8
|
|
|
10
9
|
interface CountrySelectionTemplateProps {
|
|
11
10
|
component: TemplateComponent;
|
|
12
|
-
value?:
|
|
13
|
-
onValueChange: (value: Country) => void;
|
|
11
|
+
value?: any;
|
|
12
|
+
onValueChange: (value: Country & { documentType?: string; region?: string }) => void;
|
|
14
13
|
error?: string;
|
|
15
14
|
language?: string;
|
|
16
15
|
}
|
|
17
16
|
|
|
17
|
+
type SelectionStep = 'country' | 'documentType' | 'region';
|
|
18
|
+
|
|
19
|
+
// Type étendu pour IDCardConfig avec les propriétés additionnelles
|
|
20
|
+
type ExtendedIDCardConfig = IDCardConfig & {
|
|
21
|
+
selectedCountries?: string[];
|
|
22
|
+
documentTypesByCountry?: Record<string, string[]>;
|
|
23
|
+
regionsByCountry?: Record<string, Record<string, string[]>>;
|
|
24
|
+
instructionsByDocumentType?: Record<string, LocalizedText>;
|
|
25
|
+
};
|
|
26
|
+
|
|
18
27
|
export const CountrySelectionTemplate: React.FC<CountrySelectionTemplateProps> = ({
|
|
19
28
|
component,
|
|
20
29
|
value,
|
|
@@ -22,70 +31,197 @@ export const CountrySelectionTemplate: React.FC<CountrySelectionTemplateProps> =
|
|
|
22
31
|
error,
|
|
23
32
|
language = 'en',
|
|
24
33
|
}) => {
|
|
25
|
-
const { t } = useI18n();
|
|
26
34
|
const config = component.config as CountrySelectionConfig;
|
|
27
|
-
const
|
|
28
|
-
const defaultCountry = config.default_country || 'CM';
|
|
29
|
-
const { actions, state} = useTemplateKYCFlowContext();
|
|
35
|
+
const { actions, state } = useTemplateKYCFlowContext();
|
|
30
36
|
|
|
31
|
-
|
|
37
|
+
// Récupérer les données depuis le composant id_card
|
|
38
|
+
const idCardComponent = useMemo(() => {
|
|
39
|
+
return state.template.components.find(c => c.type === 'id_card');
|
|
40
|
+
}, [state.template.components]);
|
|
32
41
|
|
|
33
|
-
const
|
|
42
|
+
const idCardConfig = useMemo(() => {
|
|
43
|
+
return idCardComponent?.config as ExtendedIDCardConfig | undefined;
|
|
44
|
+
}, [idCardComponent]);
|
|
45
|
+
|
|
46
|
+
// États de navigation - initialiser depuis value si disponible
|
|
47
|
+
const [currentStep, setCurrentStep] = useState<SelectionStep>(() => {
|
|
48
|
+
// Si value existe, on a déjà sélectionné -> aller à la dernière étape nécessaire
|
|
49
|
+
if (value?.code && value?.documentType) {
|
|
50
|
+
if (value.region) return 'region';
|
|
51
|
+
return 'documentType';
|
|
52
|
+
}
|
|
53
|
+
return 'country';
|
|
54
|
+
});
|
|
55
|
+
const [selectedCountryCode, setSelectedCountryCode] = useState<string | null>(value?.code || null);
|
|
56
|
+
const [selectedDocumentType, setSelectedDocumentType] = useState<string | null>(value?.documentType || null);
|
|
57
|
+
const [selectedRegion, setSelectedRegion] = useState<string | null>(value?.region || null);
|
|
58
|
+
|
|
59
|
+
// Restaurer l'état depuis value si le composant est remonté avec des données
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
if (value?.code && value?.documentType) {
|
|
62
|
+
setSelectedCountryCode(value.code);
|
|
63
|
+
setSelectedDocumentType(value.documentType);
|
|
64
|
+
setSelectedRegion(value.region || null);
|
|
65
|
+
// Déterminer l'étape actuelle
|
|
66
|
+
if (value.region) {
|
|
67
|
+
setCurrentStep('region');
|
|
68
|
+
} else {
|
|
69
|
+
// Vérifier si une région est nécessaire
|
|
70
|
+
const needsRegionCheck = value.code && value.documentType && idCardConfig?.regionsByCountry?.[value.code]?.[value.documentType];
|
|
71
|
+
if (needsRegionCheck && Array.isArray(needsRegionCheck) && needsRegionCheck.length > 0) {
|
|
72
|
+
setCurrentStep('region');
|
|
73
|
+
} else {
|
|
74
|
+
setCurrentStep('documentType');
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
} else {
|
|
78
|
+
// Réinitialiser si value est vide
|
|
79
|
+
setSelectedCountryCode(null);
|
|
80
|
+
setSelectedDocumentType(null);
|
|
81
|
+
setSelectedRegion(null);
|
|
82
|
+
setCurrentStep('country');
|
|
83
|
+
}
|
|
84
|
+
}, [value?.code, value?.documentType, value?.region, idCardConfig]);
|
|
85
|
+
|
|
86
|
+
// Récupérer les pays disponibles
|
|
87
|
+
const availableCountries = useMemo(() => {
|
|
88
|
+
const countries = idCardConfig?.selectedCountries || config.allowed_countries || Object.keys(countryData);
|
|
89
|
+
return countries
|
|
90
|
+
.filter((code: string) => countryData[code])
|
|
91
|
+
.map((code: string) => ({ code, ...countryData[code] }))
|
|
92
|
+
.sort((a: Country & { code?: string }, b: Country & { code?: string }) => a.name.localeCompare(b.name));
|
|
93
|
+
}, [idCardConfig?.selectedCountries, config.allowed_countries]);
|
|
94
|
+
|
|
95
|
+
// Récupérer les types de documents disponibles pour le pays sélectionné
|
|
96
|
+
const availableDocumentTypes = useMemo(() => {
|
|
97
|
+
if (!selectedCountryCode || !idCardConfig?.documentTypesByCountry) return [];
|
|
98
|
+
return idCardConfig.documentTypesByCountry[selectedCountryCode] || [];
|
|
99
|
+
}, [selectedCountryCode, idCardConfig?.documentTypesByCountry]);
|
|
100
|
+
|
|
101
|
+
// Récupérer les régions disponibles pour le pays et type de document sélectionnés
|
|
102
|
+
const availableRegions = useMemo(() => {
|
|
103
|
+
if (!selectedCountryCode || !selectedDocumentType || !idCardConfig?.regionsByCountry) return [];
|
|
104
|
+
const regions = idCardConfig.regionsByCountry[selectedCountryCode];
|
|
105
|
+
if (!regions) return [];
|
|
106
|
+
return Array.isArray(regions[selectedDocumentType]) ? regions[selectedDocumentType] : [];
|
|
107
|
+
}, [selectedCountryCode, selectedDocumentType, idCardConfig?.regionsByCountry]);
|
|
108
|
+
|
|
109
|
+
// Vérifier si une région est nécessaire
|
|
110
|
+
const needsRegion = useMemo(() => {
|
|
111
|
+
return availableRegions.length > 0;
|
|
112
|
+
}, [availableRegions]);
|
|
34
113
|
|
|
35
114
|
const getLocalizedText = (text: LocalizedText): string => {
|
|
36
|
-
return text[
|
|
115
|
+
return text[state.currentLanguage] || text.en || '';
|
|
37
116
|
};
|
|
38
117
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}))
|
|
47
|
-
.sort((a, b) => a.name.localeCompare(b.name));
|
|
118
|
+
// Navigation entre les étapes
|
|
119
|
+
const handleCountrySelect = (countryCode: string) => {
|
|
120
|
+
setSelectedCountryCode(countryCode);
|
|
121
|
+
setSelectedDocumentType(null);
|
|
122
|
+
setSelectedRegion(null);
|
|
123
|
+
setCurrentStep('documentType');
|
|
124
|
+
};
|
|
48
125
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
126
|
+
const handleDocumentTypeSelect = (docType: string) => {
|
|
127
|
+
setSelectedDocumentType(docType);
|
|
128
|
+
setSelectedRegion(null);
|
|
129
|
+
// Vérifier si une région est nécessaire (doit être recalculé après setSelectedDocumentType)
|
|
130
|
+
const regions = selectedCountryCode && idCardConfig?.regionsByCountry?.[selectedCountryCode]?.[docType];
|
|
131
|
+
if (regions && Array.isArray(regions) && regions.length > 0) {
|
|
132
|
+
setCurrentStep('region');
|
|
133
|
+
} else {
|
|
134
|
+
// Pas de région nécessaire, on peut finaliser
|
|
135
|
+
if (selectedCountryCode) {
|
|
136
|
+
finalizeSelection(selectedCountryCode, docType, null);
|
|
53
137
|
}
|
|
54
|
-
}
|
|
138
|
+
}
|
|
139
|
+
};
|
|
55
140
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
141
|
+
const handleRegionSelect = (region: string) => {
|
|
142
|
+
setSelectedRegion(region);
|
|
143
|
+
if (selectedCountryCode && selectedDocumentType) {
|
|
144
|
+
finalizeSelection(selectedCountryCode, selectedDocumentType, region);
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
const finalizeSelection = (countryCode: string, docType: string, region: string | null) => {
|
|
149
|
+
const country = countryData[countryCode];
|
|
150
|
+
if (!country) return;
|
|
151
|
+
|
|
152
|
+
const mapping = countryMapping[countryCode as keyof typeof countryMapping];
|
|
153
|
+
const countryObj: Country & { documentType?: string; region?: string } = {
|
|
154
|
+
code: countryCode,
|
|
155
|
+
...country,
|
|
156
|
+
regionMapping: mapping,
|
|
157
|
+
documentType: docType,
|
|
158
|
+
region: region || undefined,
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
onValueChange(countryObj);
|
|
162
|
+
// Passer à l'étape suivante après un court délai
|
|
163
|
+
setTimeout(() => {
|
|
164
|
+
actions.nextComponent();
|
|
165
|
+
}, 300);
|
|
166
|
+
};
|
|
61
167
|
|
|
62
|
-
|
|
63
|
-
|
|
168
|
+
const handleBack = () => {
|
|
169
|
+
if (currentStep === 'region') {
|
|
170
|
+
setCurrentStep('documentType');
|
|
171
|
+
setSelectedRegion(null);
|
|
172
|
+
} else if (currentStep === 'documentType') {
|
|
173
|
+
setCurrentStep('country');
|
|
174
|
+
setSelectedDocumentType(null);
|
|
175
|
+
setSelectedRegion(null);
|
|
176
|
+
} else if (currentStep === 'country') {
|
|
177
|
+
// Retour au composant précédent
|
|
178
|
+
actions.previousComponent();
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
const handleNext = () => {
|
|
183
|
+
if (currentStep === 'country' && selectedCountryCode) {
|
|
184
|
+
setCurrentStep('documentType');
|
|
185
|
+
} else if (currentStep === 'documentType' && selectedDocumentType) {
|
|
186
|
+
if (needsRegion) {
|
|
187
|
+
setCurrentStep('region');
|
|
188
|
+
} else {
|
|
189
|
+
if (selectedCountryCode) {
|
|
190
|
+
finalizeSelection(selectedCountryCode, selectedDocumentType, null);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
} else if (currentStep === 'region' && selectedRegion) {
|
|
194
|
+
if (selectedCountryCode && selectedDocumentType) {
|
|
195
|
+
finalizeSelection(selectedCountryCode, selectedDocumentType, selectedRegion);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
// Rendu de l'étape de sélection du pays
|
|
201
|
+
const renderCountrySelection = () => {
|
|
202
|
+
return (
|
|
203
|
+
<>
|
|
204
|
+
<ScrollView style={styles.listContainer} showsVerticalScrollIndicator={false}>
|
|
205
|
+
{availableCountries.map((country: Country & { code?: string }) => (
|
|
64
206
|
<TouchableOpacity
|
|
65
207
|
key={country.code}
|
|
66
208
|
style={[
|
|
67
|
-
styles.
|
|
68
|
-
|
|
209
|
+
styles.option,
|
|
210
|
+
selectedCountryCode === country.code && styles.selectedOption
|
|
69
211
|
]}
|
|
70
|
-
onPress={() =>
|
|
71
|
-
console.log({
|
|
72
|
-
country, language
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
setSelectedCountry(country);
|
|
76
|
-
}}
|
|
212
|
+
onPress={() => handleCountrySelect(country.code!)}
|
|
77
213
|
>
|
|
78
|
-
<Text style={styles.
|
|
79
|
-
<View style={styles.
|
|
214
|
+
<Text style={styles.flag}>{country.flag}</Text>
|
|
215
|
+
<View style={styles.optionContent}>
|
|
80
216
|
<Text style={[
|
|
81
|
-
styles.
|
|
82
|
-
|
|
217
|
+
styles.optionName,
|
|
218
|
+
selectedCountryCode === country.code && styles.selectedOptionName
|
|
83
219
|
]}>
|
|
84
|
-
{state.currentLanguage === "en"? country.name_en: country.name}
|
|
220
|
+
{state.currentLanguage === "en" ? country.name_en : country.name}
|
|
85
221
|
</Text>
|
|
86
|
-
<Text style={styles.
|
|
222
|
+
<Text style={styles.optionCode}>{country.code}</Text>
|
|
87
223
|
</View>
|
|
88
|
-
{
|
|
224
|
+
{selectedCountryCode === country.code && (
|
|
89
225
|
<View style={[
|
|
90
226
|
styles.checkmark,
|
|
91
227
|
{ backgroundColor: component.ui.themeColor as string || '#2DBD60' }
|
|
@@ -96,32 +232,201 @@ export const CountrySelectionTemplate: React.FC<CountrySelectionTemplateProps> =
|
|
|
96
232
|
</TouchableOpacity>
|
|
97
233
|
))}
|
|
98
234
|
</ScrollView>
|
|
235
|
+
</>
|
|
236
|
+
);
|
|
237
|
+
};
|
|
99
238
|
|
|
100
|
-
|
|
101
|
-
|
|
239
|
+
// Rendu de l'étape de sélection du type de document
|
|
240
|
+
const renderDocumentTypeSelection = () => {
|
|
241
|
+
const instructions = selectedDocumentType && idCardConfig?.instructionsByDocumentType?.[selectedDocumentType]
|
|
242
|
+
? getLocalizedText(idCardConfig.instructionsByDocumentType[selectedDocumentType])
|
|
243
|
+
: null;
|
|
102
244
|
|
|
245
|
+
// Mapping des labels pour l'affichage
|
|
246
|
+
const documentTypeLabels: Record<string, { en: string; fr: string; icon?: string }> = {
|
|
247
|
+
'nationalId': { en: 'National ID', fr: 'Carte nationale d\'identité', icon: '🏛️' },
|
|
248
|
+
'passport': { en: 'Passport', fr: 'Passeport', icon: '📘' },
|
|
249
|
+
'driversLicense': { en: 'Driver\'s License', fr: 'Permis de conduire', icon: '🚗' },
|
|
250
|
+
'residencePermit': { en: 'Residence Permit', fr: 'Permis de séjour', icon: '🏠' },
|
|
251
|
+
'healthInsuranceCard': { en: 'Health Insurance Card', fr: 'Carte d\'assurance maladie', icon: '🗳️' },
|
|
252
|
+
};
|
|
103
253
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
variant="primary"
|
|
111
|
-
size="large"
|
|
112
|
-
fullWidth
|
|
113
|
-
/>
|
|
254
|
+
return (
|
|
255
|
+
<>
|
|
256
|
+
{instructions && (
|
|
257
|
+
<View style={styles.infoBox}>
|
|
258
|
+
<Text style={styles.infoText}>{instructions}</Text>
|
|
259
|
+
</View>
|
|
114
260
|
)}
|
|
261
|
+
<ScrollView style={styles.listContainer} showsVerticalScrollIndicator={false}>
|
|
262
|
+
{availableDocumentTypes.map((docType: string) => {
|
|
263
|
+
const label = documentTypeLabels[docType];
|
|
264
|
+
const displayName = label ? (state.currentLanguage === "en" ? label.en : label.fr) : docType;
|
|
265
|
+
const icon = label?.icon || '📄';
|
|
115
266
|
|
|
116
|
-
|
|
117
|
-
|
|
267
|
+
return (
|
|
268
|
+
<TouchableOpacity
|
|
269
|
+
key={docType}
|
|
270
|
+
style={[
|
|
271
|
+
styles.option,
|
|
272
|
+
selectedDocumentType === docType && styles.selectedOption
|
|
273
|
+
]}
|
|
274
|
+
onPress={() => handleDocumentTypeSelect(docType)}
|
|
275
|
+
>
|
|
276
|
+
<Text style={styles.flag}>{icon}</Text>
|
|
277
|
+
<View style={styles.optionContent}>
|
|
278
|
+
<Text style={[
|
|
279
|
+
styles.optionName,
|
|
280
|
+
selectedDocumentType === docType && styles.selectedOptionName
|
|
281
|
+
]}>
|
|
282
|
+
{displayName}
|
|
283
|
+
</Text>
|
|
284
|
+
</View>
|
|
285
|
+
{selectedDocumentType === docType && (
|
|
286
|
+
<View style={[
|
|
287
|
+
styles.checkmark,
|
|
288
|
+
{ backgroundColor: component.ui.themeColor as string || '#2DBD60' }
|
|
289
|
+
]}>
|
|
290
|
+
<Text style={styles.checkmarkText}>✓</Text>
|
|
291
|
+
</View>
|
|
292
|
+
)}
|
|
293
|
+
</TouchableOpacity>
|
|
294
|
+
);
|
|
295
|
+
})}
|
|
296
|
+
</ScrollView>
|
|
297
|
+
</>
|
|
298
|
+
);
|
|
299
|
+
};
|
|
118
300
|
|
|
301
|
+
// Rendu de l'étape de sélection de la région
|
|
302
|
+
const renderRegionSelection = () => {
|
|
303
|
+
return (
|
|
304
|
+
<>
|
|
305
|
+
<ScrollView style={styles.listContainer} showsVerticalScrollIndicator={false}>
|
|
306
|
+
{availableRegions.map((region: string, index: number) => {
|
|
307
|
+
const regionLabel = typeof region === 'string' ? region : String(region);
|
|
308
|
+
return (
|
|
309
|
+
<TouchableOpacity
|
|
310
|
+
key={index}
|
|
311
|
+
style={[
|
|
312
|
+
styles.option,
|
|
313
|
+
selectedRegion === regionLabel && styles.selectedOption
|
|
314
|
+
]}
|
|
315
|
+
onPress={() => handleRegionSelect(regionLabel)}
|
|
316
|
+
>
|
|
317
|
+
<View style={styles.optionContent}>
|
|
318
|
+
<Text style={[
|
|
319
|
+
styles.optionName,
|
|
320
|
+
selectedRegion === regionLabel && styles.selectedOptionName
|
|
321
|
+
]}>
|
|
322
|
+
{regionLabel}
|
|
323
|
+
</Text>
|
|
324
|
+
</View>
|
|
325
|
+
{selectedRegion === regionLabel && (
|
|
326
|
+
<View style={[
|
|
327
|
+
styles.checkmark,
|
|
328
|
+
{ backgroundColor: component.ui.themeColor as string || '#2DBD60' }
|
|
329
|
+
]}>
|
|
330
|
+
<Text style={styles.checkmarkText}>✓</Text>
|
|
331
|
+
</View>
|
|
332
|
+
)}
|
|
333
|
+
</TouchableOpacity>
|
|
334
|
+
);
|
|
335
|
+
})}
|
|
336
|
+
</ScrollView>
|
|
337
|
+
</>
|
|
338
|
+
);
|
|
339
|
+
};
|
|
119
340
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
)
|
|
341
|
+
// Titres des étapes
|
|
342
|
+
const getStepTitle = (): string => {
|
|
343
|
+
switch (currentStep) {
|
|
344
|
+
case 'country':
|
|
345
|
+
return getLocalizedText(component.labels as LocalizedText);
|
|
346
|
+
case 'documentType':
|
|
347
|
+
return state.currentLanguage === "en" ? "Select Document Type" : "Sélectionnez le type de document";
|
|
348
|
+
case 'region':
|
|
349
|
+
return state.currentLanguage === "en" ? "Select Region" : "Sélectionnez la région";
|
|
350
|
+
default:
|
|
351
|
+
return '';
|
|
352
|
+
}
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
const getStepDescription = (): string => {
|
|
356
|
+
switch (currentStep) {
|
|
357
|
+
case 'country':
|
|
358
|
+
return getLocalizedText(component.instructions as LocalizedText);
|
|
359
|
+
case 'documentType':
|
|
360
|
+
return state.currentLanguage === "en"
|
|
361
|
+
? "Choose the type of document you want to use"
|
|
362
|
+
: "Choisissez le type de document que vous souhaitez utiliser";
|
|
363
|
+
case 'region':
|
|
364
|
+
return state.currentLanguage === "en"
|
|
365
|
+
? "Select your region"
|
|
366
|
+
: "Sélectionnez votre région";
|
|
367
|
+
default:
|
|
368
|
+
return '';
|
|
369
|
+
}
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
// Indicateur de progression
|
|
373
|
+
const getProgressStep = (): number => {
|
|
374
|
+
if (currentStep === 'country') return 1;
|
|
375
|
+
if (currentStep === 'documentType') return 2;
|
|
376
|
+
return 3;
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
const getTotalSteps = (): number => {
|
|
380
|
+
return needsRegion ? 3 : 2;
|
|
381
|
+
};
|
|
382
|
+
|
|
383
|
+
const canGoNext = (): boolean => {
|
|
384
|
+
if (currentStep === 'country') return selectedCountryCode !== null;
|
|
385
|
+
if (currentStep === 'documentType') return selectedDocumentType !== null;
|
|
386
|
+
if (currentStep === 'region') return selectedRegion !== null;
|
|
387
|
+
return false;
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
return (
|
|
391
|
+
<View style={styles.root}>
|
|
392
|
+
<View style={styles.container}>
|
|
393
|
+
{/* Indicateur de progression */}
|
|
394
|
+
<View style={styles.progressContainer}>
|
|
395
|
+
<Text style={styles.progressText}>
|
|
396
|
+
{state.currentLanguage === "en"
|
|
397
|
+
? `Step ${getProgressStep()} of ${getTotalSteps()}`
|
|
398
|
+
: `Étape ${getProgressStep()} sur ${getTotalSteps()}`}
|
|
399
|
+
</Text>
|
|
400
|
+
</View>
|
|
401
|
+
|
|
402
|
+
<Text style={styles.title}>{getStepTitle()}</Text>
|
|
403
|
+
<Text style={styles.description}>{getStepDescription()}</Text>
|
|
404
|
+
|
|
405
|
+
{/* Contenu de l'étape actuelle */}
|
|
406
|
+
{currentStep === 'country' && renderCountrySelection()}
|
|
407
|
+
{currentStep === 'documentType' && renderDocumentTypeSelection()}
|
|
408
|
+
{currentStep === 'region' && renderRegionSelection()}
|
|
409
|
+
|
|
410
|
+
{/* Boutons de navigation */}
|
|
411
|
+
<View style={styles.buttonContainer}>
|
|
412
|
+
<Button
|
|
413
|
+
title={state.currentLanguage === "en" ? "Back" : "Retour"}
|
|
414
|
+
onPress={handleBack}
|
|
415
|
+
variant={currentStep === 'country' ? "secondary" : "outline"}
|
|
416
|
+
size="large"
|
|
417
|
+
style={currentStep === 'country' ? { flex: 1 } : { flex: 1, marginRight: 8 }}
|
|
418
|
+
/>
|
|
419
|
+
{currentStep !== 'country' && (
|
|
420
|
+
<Button
|
|
421
|
+
title={state.currentLanguage === "en" ? "Next" : "Suivant"}
|
|
422
|
+
onPress={handleNext}
|
|
423
|
+
variant="primary"
|
|
424
|
+
size="large"
|
|
425
|
+
style={{ flex: 1 }}
|
|
426
|
+
disabled={!canGoNext()}
|
|
427
|
+
/>
|
|
428
|
+
)}
|
|
429
|
+
</View>
|
|
125
430
|
|
|
126
431
|
{error && (
|
|
127
432
|
<Text style={styles.errorText}>{error}</Text>
|
|
@@ -143,13 +448,20 @@ const styles = StyleSheet.create({
|
|
|
143
448
|
borderRadius: 10,
|
|
144
449
|
paddingVertical: 16,
|
|
145
450
|
paddingHorizontal: 16,
|
|
146
|
-
// shadow
|
|
147
451
|
shadowColor: '#000',
|
|
148
452
|
shadowOffset: { width: 0, height: 2 },
|
|
149
453
|
shadowOpacity: 0.35,
|
|
150
454
|
shadowRadius: 4.84,
|
|
151
455
|
elevation: 10,
|
|
152
|
-
|
|
456
|
+
},
|
|
457
|
+
progressContainer: {
|
|
458
|
+
marginBottom: 16,
|
|
459
|
+
alignItems: 'center',
|
|
460
|
+
},
|
|
461
|
+
progressText: {
|
|
462
|
+
fontSize: 14,
|
|
463
|
+
color: '#666',
|
|
464
|
+
fontWeight: '500',
|
|
153
465
|
},
|
|
154
466
|
title: {
|
|
155
467
|
fontSize: 24,
|
|
@@ -165,48 +477,41 @@ const styles = StyleSheet.create({
|
|
|
165
477
|
marginBottom: 24,
|
|
166
478
|
lineHeight: 22,
|
|
167
479
|
},
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
480
|
+
listContainer: {
|
|
481
|
+
maxHeight: 400,
|
|
482
|
+
marginBottom: 24,
|
|
171
483
|
},
|
|
172
|
-
|
|
484
|
+
option: {
|
|
173
485
|
flexDirection: 'row',
|
|
174
486
|
alignItems: 'center',
|
|
175
487
|
padding: 16,
|
|
176
|
-
backgroundColor: '
|
|
488
|
+
backgroundColor: '#F8F9FA',
|
|
177
489
|
borderRadius: 12,
|
|
178
490
|
marginBottom: 12,
|
|
179
491
|
borderWidth: 2,
|
|
180
|
-
borderColor: '#
|
|
181
|
-
|
|
182
|
-
shadowOffset: {
|
|
183
|
-
width: 0,
|
|
184
|
-
height: 2,
|
|
185
|
-
},
|
|
186
|
-
shadowOpacity: 0.1,
|
|
187
|
-
shadowRadius: 3.84,
|
|
188
|
-
elevation: 5,
|
|
492
|
+
borderColor: '#E9ECEF',
|
|
493
|
+
minHeight: 64,
|
|
189
494
|
},
|
|
190
|
-
|
|
495
|
+
selectedOption: {
|
|
191
496
|
borderColor: '#2DBD60',
|
|
192
|
-
backgroundColor: '#
|
|
497
|
+
backgroundColor: '#F0F9F0',
|
|
193
498
|
},
|
|
194
|
-
|
|
499
|
+
flag: {
|
|
195
500
|
fontSize: 32,
|
|
196
501
|
marginRight: 16,
|
|
197
502
|
},
|
|
198
|
-
|
|
503
|
+
optionContent: {
|
|
199
504
|
flex: 1,
|
|
200
505
|
},
|
|
201
|
-
|
|
506
|
+
optionName: {
|
|
202
507
|
fontSize: 18,
|
|
203
508
|
fontWeight: '600',
|
|
204
509
|
color: '#333',
|
|
205
510
|
},
|
|
206
|
-
|
|
511
|
+
selectedOptionName: {
|
|
207
512
|
color: '#2DBD60',
|
|
208
513
|
},
|
|
209
|
-
|
|
514
|
+
optionCode: {
|
|
210
515
|
fontSize: 14,
|
|
211
516
|
color: '#666',
|
|
212
517
|
marginTop: 4,
|
|
@@ -224,29 +529,21 @@ const styles = StyleSheet.create({
|
|
|
224
529
|
fontSize: 16,
|
|
225
530
|
fontWeight: 'bold',
|
|
226
531
|
},
|
|
227
|
-
|
|
228
|
-
backgroundColor: '#
|
|
229
|
-
padding:
|
|
230
|
-
borderRadius:
|
|
231
|
-
|
|
232
|
-
alignItems: 'center',
|
|
233
|
-
},
|
|
234
|
-
selectionText: {
|
|
235
|
-
fontSize: 16,
|
|
236
|
-
fontWeight: '600',
|
|
237
|
-
color: '#2DBD60',
|
|
532
|
+
infoBox: {
|
|
533
|
+
backgroundColor: '#F9FAFB',
|
|
534
|
+
padding: 12,
|
|
535
|
+
borderRadius: 12,
|
|
536
|
+
marginBottom: 16,
|
|
238
537
|
},
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
marginTop: 16,
|
|
244
|
-
alignItems: 'center',
|
|
538
|
+
infoText: {
|
|
539
|
+
fontSize: 14,
|
|
540
|
+
color: '#666',
|
|
541
|
+
lineHeight: 20,
|
|
245
542
|
},
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
543
|
+
buttonContainer: {
|
|
544
|
+
flexDirection: 'row',
|
|
545
|
+
marginTop: 24,
|
|
546
|
+
gap: 12,
|
|
250
547
|
},
|
|
251
548
|
errorText: {
|
|
252
549
|
color: '#dc2626',
|
|
@@ -254,4 +551,4 @@ const styles = StyleSheet.create({
|
|
|
254
551
|
marginTop: 8,
|
|
255
552
|
textAlign: 'center',
|
|
256
553
|
},
|
|
257
|
-
});
|
|
554
|
+
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React, { useState } from 'react';
|
|
2
|
-
import { View, Text, TouchableOpacity, StyleSheet, ScrollView, Image
|
|
2
|
+
import { View, Text, TouchableOpacity, StyleSheet, ScrollView, Image } from 'react-native';
|
|
3
|
+
import { showAlert } from '../../utils/platformAlert';
|
|
3
4
|
import { useKYCStore } from '../../stores/kycStore';
|
|
4
5
|
import NativeCameraModule from '../../modules/camera/NativeCameraModule';
|
|
5
6
|
import { KYCElement } from '../../types/KYC.types';
|
|
@@ -51,7 +52,7 @@ export const FileUpload: React.FC<FileUploadProps> = ({
|
|
|
51
52
|
|
|
52
53
|
// Vérifier le nombre maximum de fichiers
|
|
53
54
|
if (uploadedFiles.length >= uploadConfig.maxFiles) {
|
|
54
|
-
|
|
55
|
+
showAlert('Limite atteinte', `Vous ne pouvez sélectionner que ${uploadConfig.maxFiles} fichiers maximum.`);
|
|
55
56
|
return;
|
|
56
57
|
}
|
|
57
58
|
|
|
@@ -59,11 +60,11 @@ export const FileUpload: React.FC<FileUploadProps> = ({
|
|
|
59
60
|
setUploadedFiles(updatedFiles);
|
|
60
61
|
onValueChange(updatedFiles.map(file => file.uri));
|
|
61
62
|
} else if (result.error) {
|
|
62
|
-
|
|
63
|
+
showAlert('Erreur', result.error);
|
|
63
64
|
}
|
|
64
65
|
} catch (error) {
|
|
65
66
|
console.error('Erreur lors de la sélection de fichiers:', error);
|
|
66
|
-
|
|
67
|
+
showAlert('Erreur', 'Impossible de sélectionner le fichier');
|
|
67
68
|
} finally {
|
|
68
69
|
setIsUploading(false);
|
|
69
70
|
setProcessing(false);
|