@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.
Files changed (142) hide show
  1. package/android/src/main/AndroidManifest.xml +9 -4
  2. package/build/components/EnhancedCameraView.d.ts.map +1 -1
  3. package/build/components/EnhancedCameraView.js +26 -3
  4. package/build/components/EnhancedCameraView.js.map +1 -1
  5. package/build/components/EnhancedCameraView.web.d.ts.map +1 -1
  6. package/build/components/EnhancedCameraView.web.js +21 -0
  7. package/build/components/EnhancedCameraView.web.js.map +1 -1
  8. package/build/components/KYCElements/CameraCapture.d.ts.map +1 -1
  9. package/build/components/KYCElements/CameraCapture.js +4 -3
  10. package/build/components/KYCElements/CameraCapture.js.map +1 -1
  11. package/build/components/KYCElements/CountrySelectionTemplate.d.ts +5 -2
  12. package/build/components/KYCElements/CountrySelectionTemplate.d.ts.map +1 -1
  13. package/build/components/KYCElements/CountrySelectionTemplate.js +360 -101
  14. package/build/components/KYCElements/CountrySelectionTemplate.js.map +1 -1
  15. package/build/components/KYCElements/FileUpload.d.ts.map +1 -1
  16. package/build/components/KYCElements/FileUpload.js +5 -4
  17. package/build/components/KYCElements/FileUpload.js.map +1 -1
  18. package/build/components/KYCElements/FileUploadTemplate.d.ts.map +1 -1
  19. package/build/components/KYCElements/FileUploadTemplate.js +5 -4
  20. package/build/components/KYCElements/FileUploadTemplate.js.map +1 -1
  21. package/build/components/KYCElements/IDCardCapture.d.ts.map +1 -1
  22. package/build/components/KYCElements/IDCardCapture.js +193 -237
  23. package/build/components/KYCElements/IDCardCapture.js.map +1 -1
  24. package/build/components/KYCElements/LocationCaptureTemplate.d.ts.map +1 -1
  25. package/build/components/KYCElements/LocationCaptureTemplate.js +78 -37
  26. package/build/components/KYCElements/LocationCaptureTemplate.js.map +1 -1
  27. package/build/components/KYCElements/OrientationVideoCapture.js +3 -2
  28. package/build/components/KYCElements/OrientationVideoCapture.js.map +1 -1
  29. package/build/components/KYCElements/OrientationVideoCaptureEnhanced.js +3 -2
  30. package/build/components/KYCElements/OrientationVideoCaptureEnhanced.js.map +1 -1
  31. package/build/components/KYCElements/OrientationVideoCaptureFinal.js +3 -2
  32. package/build/components/KYCElements/OrientationVideoCaptureFinal.js.map +1 -1
  33. package/build/components/KYCElements/SelfieCapture.d.ts.map +1 -1
  34. package/build/components/KYCElements/SelfieCapture.js +4 -3
  35. package/build/components/KYCElements/SelfieCapture.js.map +1 -1
  36. package/build/components/KYCElements/SelfieCaptureTemplate.d.ts.map +1 -1
  37. package/build/components/KYCElements/SelfieCaptureTemplate.js +182 -39
  38. package/build/components/KYCElements/SelfieCaptureTemplate.js.map +1 -1
  39. package/build/components/KYCElements/WelcomeTemplate.d.ts +12 -0
  40. package/build/components/KYCElements/WelcomeTemplate.d.ts.map +1 -0
  41. package/build/components/KYCElements/WelcomeTemplate.js +243 -0
  42. package/build/components/KYCElements/WelcomeTemplate.js.map +1 -0
  43. package/build/components/TemplateKYCExample.d.ts +4 -2
  44. package/build/components/TemplateKYCExample.d.ts.map +1 -1
  45. package/build/components/TemplateKYCExample.js +5 -68
  46. package/build/components/TemplateKYCExample.js.map +1 -1
  47. package/build/components/TemplateKYCFlowRefactored.d.ts +2 -1
  48. package/build/components/TemplateKYCFlowRefactored.d.ts.map +1 -1
  49. package/build/components/TemplateKYCFlowRefactored.js +95 -9
  50. package/build/components/TemplateKYCFlowRefactored.js.map +1 -1
  51. package/build/components/example/DynamicTemplateExample.d.ts +10 -0
  52. package/build/components/example/DynamicTemplateExample.d.ts.map +1 -0
  53. package/build/components/example/DynamicTemplateExample.js +241 -0
  54. package/build/components/example/DynamicTemplateExample.js.map +1 -0
  55. package/build/config/allowedDomains.d.ts +30 -0
  56. package/build/config/allowedDomains.d.ts.map +1 -0
  57. package/build/config/allowedDomains.js +127 -0
  58. package/build/config/allowedDomains.js.map +1 -0
  59. package/build/hooks/useTemplateKYCFlow.d.ts.map +1 -1
  60. package/build/hooks/useTemplateKYCFlow.js +68 -43
  61. package/build/hooks/useTemplateKYCFlow.js.map +1 -1
  62. package/build/hooks/useTemplateLoader.d.ts +14 -0
  63. package/build/hooks/useTemplateLoader.d.ts.map +1 -0
  64. package/build/hooks/useTemplateLoader.js +85 -0
  65. package/build/hooks/useTemplateLoader.js.map +1 -0
  66. package/build/i18n/en/index.d.ts +9 -0
  67. package/build/i18n/en/index.d.ts.map +1 -1
  68. package/build/i18n/en/index.js +9 -0
  69. package/build/i18n/en/index.js.map +1 -1
  70. package/build/i18n/fr/index.d.ts +9 -0
  71. package/build/i18n/fr/index.d.ts.map +1 -1
  72. package/build/i18n/fr/index.js +9 -0
  73. package/build/i18n/fr/index.js.map +1 -1
  74. package/build/index.d.ts +5 -0
  75. package/build/index.d.ts.map +1 -1
  76. package/build/index.js +8 -0
  77. package/build/index.js.map +1 -1
  78. package/build/modules/api/CardAuthentification.js +1 -0
  79. package/build/modules/api/CardAuthentification.js.map +1 -1
  80. package/build/modules/api/KYCService.d.ts +4 -1
  81. package/build/modules/api/KYCService.d.ts.map +1 -1
  82. package/build/modules/api/KYCService.js +17 -5
  83. package/build/modules/api/KYCService.js.map +1 -1
  84. package/build/modules/api/TemplateService.d.ts +45 -0
  85. package/build/modules/api/TemplateService.d.ts.map +1 -0
  86. package/build/modules/api/TemplateService.js +145 -0
  87. package/build/modules/api/TemplateService.js.map +1 -0
  88. package/build/modules/api/types.d.ts +1 -0
  89. package/build/modules/api/types.d.ts.map +1 -1
  90. package/build/modules/api/types.js.map +1 -1
  91. package/build/types/KYC.types.d.ts +144 -4
  92. package/build/types/KYC.types.d.ts.map +1 -1
  93. package/build/types/KYC.types.js +15 -0
  94. package/build/types/KYC.types.js.map +1 -1
  95. package/build/utils/cropByObb.d.ts +1 -0
  96. package/build/utils/cropByObb.d.ts.map +1 -1
  97. package/build/utils/cropByObb.js +70 -0
  98. package/build/utils/cropByObb.js.map +1 -1
  99. package/build/utils/platformAlert.d.ts +20 -0
  100. package/build/utils/platformAlert.d.ts.map +1 -0
  101. package/build/utils/platformAlert.js +67 -0
  102. package/build/utils/platformAlert.js.map +1 -0
  103. package/build/utils/template-transformer.d.ts +10 -0
  104. package/build/utils/template-transformer.d.ts.map +1 -0
  105. package/build/utils/template-transformer.js +353 -0
  106. package/build/utils/template-transformer.js.map +1 -0
  107. package/build/web/WebKYCEntry.d.ts.map +1 -1
  108. package/build/web/WebKYCEntry.js +102 -20
  109. package/build/web/WebKYCEntry.js.map +1 -1
  110. package/package.json +1 -1
  111. package/src/components/EnhancedCameraView.tsx +31 -2
  112. package/src/components/EnhancedCameraView.web.tsx +24 -0
  113. package/src/components/KYCElements/CameraCapture.tsx +4 -3
  114. package/src/components/KYCElements/CountrySelectionTemplate.tsx +410 -113
  115. package/src/components/KYCElements/FileUpload.tsx +5 -4
  116. package/src/components/KYCElements/FileUploadTemplate.tsx +5 -4
  117. package/src/components/KYCElements/IDCardCapture.tsx +196 -254
  118. package/src/components/KYCElements/LocationCaptureTemplate.tsx +95 -44
  119. package/src/components/KYCElements/OrientationVideoCapture.tsx +2 -2
  120. package/src/components/KYCElements/OrientationVideoCaptureEnhanced.tsx +2 -2
  121. package/src/components/KYCElements/OrientationVideoCaptureFinal.tsx +2 -2
  122. package/src/components/KYCElements/SelfieCapture.tsx +4 -3
  123. package/src/components/KYCElements/SelfieCaptureTemplate.tsx +195 -41
  124. package/src/components/KYCElements/WelcomeTemplate.tsx +289 -0
  125. package/src/components/TemplateKYCExample.tsx +16 -71
  126. package/src/components/TemplateKYCFlowRefactored.tsx +121 -11
  127. package/src/components/example/DynamicTemplateExample.tsx +289 -0
  128. package/src/config/allowedDomains.ts +152 -0
  129. package/src/hooks/useTemplateKYCFlow.tsx +71 -46
  130. package/src/hooks/useTemplateLoader.ts +102 -0
  131. package/src/i18n/en/index.ts +10 -0
  132. package/src/i18n/fr/index.ts +9 -0
  133. package/src/index.ts +11 -0
  134. package/src/modules/api/CardAuthentification.ts +1 -1
  135. package/src/modules/api/KYCService.ts +18 -8
  136. package/src/modules/api/TemplateService.ts +167 -0
  137. package/src/modules/api/types.ts +1 -0
  138. package/src/types/KYC.types.ts +188 -3
  139. package/src/utils/cropByObb.ts +83 -3
  140. package/src/utils/platformAlert.ts +85 -0
  141. package/src/utils/template-transformer.ts +433 -0
  142. package/src/web/WebKYCEntry.tsx +122 -24
@@ -1,20 +1,29 @@
1
- import React, { useEffect, useState } from '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?: string;
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 allowedCountries = (config.allowed_countries || Object.keys(countryData));
28
- const defaultCountry = config.default_country || 'CM';
29
- const { actions, state} = useTemplateKYCFlowContext();
35
+ const { actions, state } = useTemplateKYCFlowContext();
30
36
 
31
- console.log({ language }, state.currentLanguage);
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 [selectedCountry, setSelectedCountry] = useState<Country>({ code: defaultCountry, ...countryData[defaultCountry] });
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[language] || text.en || '';
115
+ return text[state.currentLanguage] || text.en || '';
37
116
  };
38
117
 
39
- const selectedValue = value || defaultCountry;
40
-
41
- const filteredCountries = allowedCountries
42
- .filter(code => countryData[code])
43
- .map(code => ({
44
- code,
45
- ...countryData[code]
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
- useEffect(() => {
50
- if (selectedCountry) {
51
- const mapping = selectedCountry.code ? countryMapping[selectedCountry.code as keyof typeof countryMapping] : undefined;
52
- onValueChange({ ...selectedCountry, regionMapping: mapping });
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
- }, [selectedCountry]);
138
+ }
139
+ };
55
140
 
56
- return (
57
- <View style={styles.root}>
58
- <View style={styles.container}>
59
- <Text style={styles.title}>{getLocalizedText(component.labels as LocalizedText)}</Text>
60
- <Text style={styles.description}>{getLocalizedText(component.instructions as LocalizedText)}</Text>
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
- <ScrollView style={styles.countriesContainer} showsVerticalScrollIndicator={false}>
63
- {filteredCountries.map((country) => (
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.countryOption,
68
- selectedCountry.code === country.code && styles.selectedCountryOption
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.countryFlag}>{country.flag}</Text>
79
- <View style={styles.countryContent}>
214
+ <Text style={styles.flag}>{country.flag}</Text>
215
+ <View style={styles.optionContent}>
80
216
  <Text style={[
81
- styles.countryName,
82
- selectedCountry.code === country.code && styles.selectedCountryName
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.countryCode}>{country.code}</Text>
222
+ <Text style={styles.optionCode}>{country.code}</Text>
87
223
  </View>
88
- {selectedCountry.code === country.code && (
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
- {selectedCountry.code && (
101
- <Button title={t('common.next')} onPress={() => {
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
- const mapping = selectedCountry.code ? countryMapping[selectedCountry.code as keyof typeof countryMapping] : undefined;
105
- onValueChange({ ...selectedCountry, regionMapping: mapping });
106
- setTimeout(() => {
107
- actions.nextComponent();
108
- }, 500);
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
- {config.required && !selectedValue && (
117
- <View style={styles.requiredContainer}>
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
- <Text style={styles.requiredText}>{
121
- state.currentLanguage === "en" ? "⚠️ Please Select your country" : "⚠️ Veuillez sélectionner un pays"
122
- }</Text>
123
- </View>
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
- countriesContainer: {
169
- height: "80%",
170
- maxHeight: 600,
480
+ listContainer: {
481
+ maxHeight: 400,
482
+ marginBottom: 24,
171
483
  },
172
- countryOption: {
484
+ option: {
173
485
  flexDirection: 'row',
174
486
  alignItems: 'center',
175
487
  padding: 16,
176
- backgroundColor: 'white',
488
+ backgroundColor: '#F8F9FA',
177
489
  borderRadius: 12,
178
490
  marginBottom: 12,
179
491
  borderWidth: 2,
180
- borderColor: '#e5e5e5',
181
- shadowColor: '#000',
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
- selectedCountryOption: {
495
+ selectedOption: {
191
496
  borderColor: '#2DBD60',
192
- backgroundColor: '#f0f9f0',
497
+ backgroundColor: '#F0F9F0',
193
498
  },
194
- countryFlag: {
499
+ flag: {
195
500
  fontSize: 32,
196
501
  marginRight: 16,
197
502
  },
198
- countryContent: {
503
+ optionContent: {
199
504
  flex: 1,
200
505
  },
201
- countryName: {
506
+ optionName: {
202
507
  fontSize: 18,
203
508
  fontWeight: '600',
204
509
  color: '#333',
205
510
  },
206
- selectedCountryName: {
511
+ selectedOptionName: {
207
512
  color: '#2DBD60',
208
513
  },
209
- countryCode: {
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
- selectionInfo: {
228
- backgroundColor: '#f0f9f0',
229
- padding: 16,
230
- borderRadius: 8,
231
- marginTop: 16,
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
- requiredContainer: {
240
- backgroundColor: '#fff3cd',
241
- padding: 16,
242
- borderRadius: 8,
243
- marginTop: 16,
244
- alignItems: 'center',
538
+ infoText: {
539
+ fontSize: 14,
540
+ color: '#666',
541
+ lineHeight: 20,
245
542
  },
246
- requiredText: {
247
- fontSize: 16,
248
- fontWeight: '600',
249
- color: '#856404',
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, Alert } from 'react-native';
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
- Alert.alert('Limite atteinte', `Vous ne pouvez sélectionner que ${uploadConfig.maxFiles} fichiers maximum.`);
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
- Alert.alert('Erreur', result.error);
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
- Alert.alert('Erreur', 'Impossible de sélectionner le fichier');
67
+ showAlert('Erreur', 'Impossible de sélectionner le fichier');
67
68
  } finally {
68
69
  setIsUploading(false);
69
70
  setProcessing(false);