@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,82 +1,348 @@
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
3
  import { countryMapping } from '../../config/region_mapping';
4
4
  import { useTemplateKYCFlowContext } from '../../hooks/useTemplateKYCFlow';
5
- import { useI18n } from '../../hooks/useI18n';
6
5
  import { Button } from '../ui/Button';
7
6
  import { countryData } from '../../config/countriesData';
8
7
  export const CountrySelectionTemplate = ({ component, value, onValueChange, error, language = 'en', }) => {
9
- const { t } = useI18n();
10
8
  const config = component.config;
11
- const allowedCountries = (config.allowed_countries || Object.keys(countryData));
12
- const defaultCountry = config.default_country || 'CM';
13
9
  const { actions, state } = useTemplateKYCFlowContext();
14
- console.log({ language }, state.currentLanguage);
15
- const [selectedCountry, setSelectedCountry] = useState({ code: defaultCountry, ...countryData[defaultCountry] });
10
+ // Récupérer les données depuis le composant id_card
11
+ const idCardComponent = useMemo(() => {
12
+ return state.template.components.find(c => c.type === 'id_card');
13
+ }, [state.template.components]);
14
+ const idCardConfig = useMemo(() => {
15
+ return idCardComponent?.config;
16
+ }, [idCardComponent]);
17
+ // États de navigation - initialiser depuis value si disponible
18
+ const [currentStep, setCurrentStep] = useState(() => {
19
+ // Si value existe, on a déjà sélectionné -> aller à la dernière étape nécessaire
20
+ if (value?.code && value?.documentType) {
21
+ if (value.region)
22
+ return 'region';
23
+ return 'documentType';
24
+ }
25
+ return 'country';
26
+ });
27
+ const [selectedCountryCode, setSelectedCountryCode] = useState(value?.code || null);
28
+ const [selectedDocumentType, setSelectedDocumentType] = useState(value?.documentType || null);
29
+ const [selectedRegion, setSelectedRegion] = useState(value?.region || null);
30
+ // Restaurer l'état depuis value si le composant est remonté avec des données
31
+ useEffect(() => {
32
+ if (value?.code && value?.documentType) {
33
+ setSelectedCountryCode(value.code);
34
+ setSelectedDocumentType(value.documentType);
35
+ setSelectedRegion(value.region || null);
36
+ // Déterminer l'étape actuelle
37
+ if (value.region) {
38
+ setCurrentStep('region');
39
+ }
40
+ else {
41
+ // Vérifier si une région est nécessaire
42
+ const needsRegionCheck = value.code && value.documentType && idCardConfig?.regionsByCountry?.[value.code]?.[value.documentType];
43
+ if (needsRegionCheck && Array.isArray(needsRegionCheck) && needsRegionCheck.length > 0) {
44
+ setCurrentStep('region');
45
+ }
46
+ else {
47
+ setCurrentStep('documentType');
48
+ }
49
+ }
50
+ }
51
+ else {
52
+ // Réinitialiser si value est vide
53
+ setSelectedCountryCode(null);
54
+ setSelectedDocumentType(null);
55
+ setSelectedRegion(null);
56
+ setCurrentStep('country');
57
+ }
58
+ }, [value?.code, value?.documentType, value?.region, idCardConfig]);
59
+ // Récupérer les pays disponibles
60
+ const availableCountries = useMemo(() => {
61
+ const countries = idCardConfig?.selectedCountries || config.allowed_countries || Object.keys(countryData);
62
+ return countries
63
+ .filter((code) => countryData[code])
64
+ .map((code) => ({ code, ...countryData[code] }))
65
+ .sort((a, b) => a.name.localeCompare(b.name));
66
+ }, [idCardConfig?.selectedCountries, config.allowed_countries]);
67
+ // Récupérer les types de documents disponibles pour le pays sélectionné
68
+ const availableDocumentTypes = useMemo(() => {
69
+ if (!selectedCountryCode || !idCardConfig?.documentTypesByCountry)
70
+ return [];
71
+ return idCardConfig.documentTypesByCountry[selectedCountryCode] || [];
72
+ }, [selectedCountryCode, idCardConfig?.documentTypesByCountry]);
73
+ // Récupérer les régions disponibles pour le pays et type de document sélectionnés
74
+ const availableRegions = useMemo(() => {
75
+ if (!selectedCountryCode || !selectedDocumentType || !idCardConfig?.regionsByCountry)
76
+ return [];
77
+ const regions = idCardConfig.regionsByCountry[selectedCountryCode];
78
+ if (!regions)
79
+ return [];
80
+ return Array.isArray(regions[selectedDocumentType]) ? regions[selectedDocumentType] : [];
81
+ }, [selectedCountryCode, selectedDocumentType, idCardConfig?.regionsByCountry]);
82
+ // Vérifier si une région est nécessaire
83
+ const needsRegion = useMemo(() => {
84
+ return availableRegions.length > 0;
85
+ }, [availableRegions]);
16
86
  const getLocalizedText = (text) => {
17
- return text[language] || text.en || '';
87
+ return text[state.currentLanguage] || text.en || '';
18
88
  };
19
- const selectedValue = value || defaultCountry;
20
- const filteredCountries = allowedCountries
21
- .filter(code => countryData[code])
22
- .map(code => ({
23
- code,
24
- ...countryData[code]
25
- }))
26
- .sort((a, b) => a.name.localeCompare(b.name));
27
- useEffect(() => {
28
- if (selectedCountry) {
29
- const mapping = selectedCountry.code ? countryMapping[selectedCountry.code] : undefined;
30
- onValueChange({ ...selectedCountry, regionMapping: mapping });
89
+ // Navigation entre les étapes
90
+ const handleCountrySelect = (countryCode) => {
91
+ setSelectedCountryCode(countryCode);
92
+ setSelectedDocumentType(null);
93
+ setSelectedRegion(null);
94
+ setCurrentStep('documentType');
95
+ };
96
+ const handleDocumentTypeSelect = (docType) => {
97
+ setSelectedDocumentType(docType);
98
+ setSelectedRegion(null);
99
+ // Vérifier si une région est nécessaire (doit être recalculé après setSelectedDocumentType)
100
+ const regions = selectedCountryCode && idCardConfig?.regionsByCountry?.[selectedCountryCode]?.[docType];
101
+ if (regions && Array.isArray(regions) && regions.length > 0) {
102
+ setCurrentStep('region');
31
103
  }
32
- }, [selectedCountry]);
33
- return (<View style={styles.root}>
34
- <View style={styles.container}>
35
- <Text style={styles.title}>{getLocalizedText(component.labels)}</Text>
36
- <Text style={styles.description}>{getLocalizedText(component.instructions)}</Text>
37
-
38
- <ScrollView style={styles.countriesContainer} showsVerticalScrollIndicator={false}>
39
- {filteredCountries.map((country) => (<TouchableOpacity key={country.code} style={[
40
- styles.countryOption,
41
- selectedCountry.code === country.code && styles.selectedCountryOption
42
- ]} onPress={() => {
43
- console.log({
44
- country, language
45
- });
46
- setSelectedCountry(country);
47
- }}>
48
- <Text style={styles.countryFlag}>{country.flag}</Text>
49
- <View style={styles.countryContent}>
104
+ else {
105
+ // Pas de région nécessaire, on peut finaliser
106
+ if (selectedCountryCode) {
107
+ finalizeSelection(selectedCountryCode, docType, null);
108
+ }
109
+ }
110
+ };
111
+ const handleRegionSelect = (region) => {
112
+ setSelectedRegion(region);
113
+ if (selectedCountryCode && selectedDocumentType) {
114
+ finalizeSelection(selectedCountryCode, selectedDocumentType, region);
115
+ }
116
+ };
117
+ const finalizeSelection = (countryCode, docType, region) => {
118
+ const country = countryData[countryCode];
119
+ if (!country)
120
+ return;
121
+ const mapping = countryMapping[countryCode];
122
+ const countryObj = {
123
+ code: countryCode,
124
+ ...country,
125
+ regionMapping: mapping,
126
+ documentType: docType,
127
+ region: region || undefined,
128
+ };
129
+ onValueChange(countryObj);
130
+ // Passer à l'étape suivante après un court délai
131
+ setTimeout(() => {
132
+ actions.nextComponent();
133
+ }, 300);
134
+ };
135
+ const handleBack = () => {
136
+ if (currentStep === 'region') {
137
+ setCurrentStep('documentType');
138
+ setSelectedRegion(null);
139
+ }
140
+ else if (currentStep === 'documentType') {
141
+ setCurrentStep('country');
142
+ setSelectedDocumentType(null);
143
+ setSelectedRegion(null);
144
+ }
145
+ else if (currentStep === 'country') {
146
+ // Retour au composant précédent
147
+ actions.previousComponent();
148
+ }
149
+ };
150
+ const handleNext = () => {
151
+ if (currentStep === 'country' && selectedCountryCode) {
152
+ setCurrentStep('documentType');
153
+ }
154
+ else if (currentStep === 'documentType' && selectedDocumentType) {
155
+ if (needsRegion) {
156
+ setCurrentStep('region');
157
+ }
158
+ else {
159
+ if (selectedCountryCode) {
160
+ finalizeSelection(selectedCountryCode, selectedDocumentType, null);
161
+ }
162
+ }
163
+ }
164
+ else if (currentStep === 'region' && selectedRegion) {
165
+ if (selectedCountryCode && selectedDocumentType) {
166
+ finalizeSelection(selectedCountryCode, selectedDocumentType, selectedRegion);
167
+ }
168
+ }
169
+ };
170
+ // Rendu de l'étape de sélection du pays
171
+ const renderCountrySelection = () => {
172
+ return (<>
173
+ <ScrollView style={styles.listContainer} showsVerticalScrollIndicator={false}>
174
+ {availableCountries.map((country) => (<TouchableOpacity key={country.code} style={[
175
+ styles.option,
176
+ selectedCountryCode === country.code && styles.selectedOption
177
+ ]} onPress={() => handleCountrySelect(country.code)}>
178
+ <Text style={styles.flag}>{country.flag}</Text>
179
+ <View style={styles.optionContent}>
50
180
  <Text style={[
51
- styles.countryName,
52
- selectedCountry.code === country.code && styles.selectedCountryName
53
- ]}>
181
+ styles.optionName,
182
+ selectedCountryCode === country.code && styles.selectedOptionName
183
+ ]}>
54
184
  {state.currentLanguage === "en" ? country.name_en : country.name}
55
185
  </Text>
56
- <Text style={styles.countryCode}>{country.code}</Text>
186
+ <Text style={styles.optionCode}>{country.code}</Text>
57
187
  </View>
58
- {selectedCountry.code === country.code && (<View style={[
59
- styles.checkmark,
60
- { backgroundColor: component.ui.themeColor || '#2DBD60' }
61
- ]}>
188
+ {selectedCountryCode === country.code && (<View style={[
189
+ styles.checkmark,
190
+ { backgroundColor: component.ui.themeColor || '#2DBD60' }
191
+ ]}>
62
192
  <Text style={styles.checkmarkText}>✓</Text>
63
193
  </View>)}
64
194
  </TouchableOpacity>))}
65
195
  </ScrollView>
196
+ </>);
197
+ };
198
+ // Rendu de l'étape de sélection du type de document
199
+ const renderDocumentTypeSelection = () => {
200
+ const instructions = selectedDocumentType && idCardConfig?.instructionsByDocumentType?.[selectedDocumentType]
201
+ ? getLocalizedText(idCardConfig.instructionsByDocumentType[selectedDocumentType])
202
+ : null;
203
+ // Mapping des labels pour l'affichage
204
+ const documentTypeLabels = {
205
+ 'nationalId': { en: 'National ID', fr: 'Carte nationale d\'identité', icon: '🏛️' },
206
+ 'passport': { en: 'Passport', fr: 'Passeport', icon: '📘' },
207
+ 'driversLicense': { en: 'Driver\'s License', fr: 'Permis de conduire', icon: '🚗' },
208
+ 'residencePermit': { en: 'Residence Permit', fr: 'Permis de séjour', icon: '🏠' },
209
+ 'healthInsuranceCard': { en: 'Health Insurance Card', fr: 'Carte d\'assurance maladie', icon: '🗳️' },
210
+ };
211
+ return (<>
212
+ {instructions && (<View style={styles.infoBox}>
213
+ <Text style={styles.infoText}>{instructions}</Text>
214
+ </View>)}
215
+ <ScrollView style={styles.listContainer} showsVerticalScrollIndicator={false}>
216
+ {availableDocumentTypes.map((docType) => {
217
+ const label = documentTypeLabels[docType];
218
+ const displayName = label ? (state.currentLanguage === "en" ? label.en : label.fr) : docType;
219
+ const icon = label?.icon || '📄';
220
+ return (<TouchableOpacity key={docType} style={[
221
+ styles.option,
222
+ selectedDocumentType === docType && styles.selectedOption
223
+ ]} onPress={() => handleDocumentTypeSelect(docType)}>
224
+ <Text style={styles.flag}>{icon}</Text>
225
+ <View style={styles.optionContent}>
226
+ <Text style={[
227
+ styles.optionName,
228
+ selectedDocumentType === docType && styles.selectedOptionName
229
+ ]}>
230
+ {displayName}
231
+ </Text>
232
+ </View>
233
+ {selectedDocumentType === docType && (<View style={[
234
+ styles.checkmark,
235
+ { backgroundColor: component.ui.themeColor || '#2DBD60' }
236
+ ]}>
237
+ <Text style={styles.checkmarkText}>✓</Text>
238
+ </View>)}
239
+ </TouchableOpacity>);
240
+ })}
241
+ </ScrollView>
242
+ </>);
243
+ };
244
+ // Rendu de l'étape de sélection de la région
245
+ const renderRegionSelection = () => {
246
+ return (<>
247
+ <ScrollView style={styles.listContainer} showsVerticalScrollIndicator={false}>
248
+ {availableRegions.map((region, index) => {
249
+ const regionLabel = typeof region === 'string' ? region : String(region);
250
+ return (<TouchableOpacity key={index} style={[
251
+ styles.option,
252
+ selectedRegion === regionLabel && styles.selectedOption
253
+ ]} onPress={() => handleRegionSelect(regionLabel)}>
254
+ <View style={styles.optionContent}>
255
+ <Text style={[
256
+ styles.optionName,
257
+ selectedRegion === regionLabel && styles.selectedOptionName
258
+ ]}>
259
+ {regionLabel}
260
+ </Text>
261
+ </View>
262
+ {selectedRegion === regionLabel && (<View style={[
263
+ styles.checkmark,
264
+ { backgroundColor: component.ui.themeColor || '#2DBD60' }
265
+ ]}>
266
+ <Text style={styles.checkmarkText}>✓</Text>
267
+ </View>)}
268
+ </TouchableOpacity>);
269
+ })}
270
+ </ScrollView>
271
+ </>);
272
+ };
273
+ // Titres des étapes
274
+ const getStepTitle = () => {
275
+ switch (currentStep) {
276
+ case 'country':
277
+ return getLocalizedText(component.labels);
278
+ case 'documentType':
279
+ return state.currentLanguage === "en" ? "Select Document Type" : "Sélectionnez le type de document";
280
+ case 'region':
281
+ return state.currentLanguage === "en" ? "Select Region" : "Sélectionnez la région";
282
+ default:
283
+ return '';
284
+ }
285
+ };
286
+ const getStepDescription = () => {
287
+ switch (currentStep) {
288
+ case 'country':
289
+ return getLocalizedText(component.instructions);
290
+ case 'documentType':
291
+ return state.currentLanguage === "en"
292
+ ? "Choose the type of document you want to use"
293
+ : "Choisissez le type de document que vous souhaitez utiliser";
294
+ case 'region':
295
+ return state.currentLanguage === "en"
296
+ ? "Select your region"
297
+ : "Sélectionnez votre région";
298
+ default:
299
+ return '';
300
+ }
301
+ };
302
+ // Indicateur de progression
303
+ const getProgressStep = () => {
304
+ if (currentStep === 'country')
305
+ return 1;
306
+ if (currentStep === 'documentType')
307
+ return 2;
308
+ return 3;
309
+ };
310
+ const getTotalSteps = () => {
311
+ return needsRegion ? 3 : 2;
312
+ };
313
+ const canGoNext = () => {
314
+ if (currentStep === 'country')
315
+ return selectedCountryCode !== null;
316
+ if (currentStep === 'documentType')
317
+ return selectedDocumentType !== null;
318
+ if (currentStep === 'region')
319
+ return selectedRegion !== null;
320
+ return false;
321
+ };
322
+ return (<View style={styles.root}>
323
+ <View style={styles.container}>
324
+ {/* Indicateur de progression */}
325
+ <View style={styles.progressContainer}>
326
+ <Text style={styles.progressText}>
327
+ {state.currentLanguage === "en"
328
+ ? `Step ${getProgressStep()} of ${getTotalSteps()}`
329
+ : `Étape ${getProgressStep()} sur ${getTotalSteps()}`}
330
+ </Text>
331
+ </View>
66
332
 
67
- {selectedCountry.code && (<Button title={t('common.next')} onPress={() => {
68
- const mapping = selectedCountry.code ? countryMapping[selectedCountry.code] : undefined;
69
- onValueChange({ ...selectedCountry, regionMapping: mapping });
70
- setTimeout(() => {
71
- actions.nextComponent();
72
- }, 500);
73
- }} variant="primary" size="large" fullWidth/>)}
74
-
75
- {config.required && !selectedValue && (<View style={styles.requiredContainer}>
333
+ <Text style={styles.title}>{getStepTitle()}</Text>
334
+ <Text style={styles.description}>{getStepDescription()}</Text>
76
335
 
336
+ {/* Contenu de l'étape actuelle */}
337
+ {currentStep === 'country' && renderCountrySelection()}
338
+ {currentStep === 'documentType' && renderDocumentTypeSelection()}
339
+ {currentStep === 'region' && renderRegionSelection()}
77
340
 
78
- <Text style={styles.requiredText}>{state.currentLanguage === "en" ? "⚠️ Please Select your country" : "⚠️ Veuillez sélectionner un pays"}</Text>
79
- </View>)}
341
+ {/* Boutons de navigation */}
342
+ <View style={styles.buttonContainer}>
343
+ <Button title={state.currentLanguage === "en" ? "Back" : "Retour"} onPress={handleBack} variant={currentStep === 'country' ? "secondary" : "outline"} size="large" style={currentStep === 'country' ? { flex: 1 } : { flex: 1, marginRight: 8 }}/>
344
+ {currentStep !== 'country' && (<Button title={state.currentLanguage === "en" ? "Next" : "Suivant"} onPress={handleNext} variant="primary" size="large" style={{ flex: 1 }} disabled={!canGoNext()}/>)}
345
+ </View>
80
346
 
81
347
  {error && (<Text style={styles.errorText}>{error}</Text>)}
82
348
  </View>
@@ -94,13 +360,21 @@ const styles = StyleSheet.create({
94
360
  borderRadius: 10,
95
361
  paddingVertical: 16,
96
362
  paddingHorizontal: 16,
97
- // shadow
98
363
  shadowColor: '#000',
99
364
  shadowOffset: { width: 0, height: 2 },
100
365
  shadowOpacity: 0.35,
101
366
  shadowRadius: 4.84,
102
367
  elevation: 10,
103
368
  },
369
+ progressContainer: {
370
+ marginBottom: 16,
371
+ alignItems: 'center',
372
+ },
373
+ progressText: {
374
+ fontSize: 14,
375
+ color: '#666',
376
+ fontWeight: '500',
377
+ },
104
378
  title: {
105
379
  fontSize: 24,
106
380
  fontWeight: 'bold',
@@ -115,48 +389,41 @@ const styles = StyleSheet.create({
115
389
  marginBottom: 24,
116
390
  lineHeight: 22,
117
391
  },
118
- countriesContainer: {
119
- height: "80%",
120
- maxHeight: 600,
392
+ listContainer: {
393
+ maxHeight: 400,
394
+ marginBottom: 24,
121
395
  },
122
- countryOption: {
396
+ option: {
123
397
  flexDirection: 'row',
124
398
  alignItems: 'center',
125
399
  padding: 16,
126
- backgroundColor: 'white',
400
+ backgroundColor: '#F8F9FA',
127
401
  borderRadius: 12,
128
402
  marginBottom: 12,
129
403
  borderWidth: 2,
130
- borderColor: '#e5e5e5',
131
- shadowColor: '#000',
132
- shadowOffset: {
133
- width: 0,
134
- height: 2,
135
- },
136
- shadowOpacity: 0.1,
137
- shadowRadius: 3.84,
138
- elevation: 5,
139
- },
140
- selectedCountryOption: {
404
+ borderColor: '#E9ECEF',
405
+ minHeight: 64,
406
+ },
407
+ selectedOption: {
141
408
  borderColor: '#2DBD60',
142
- backgroundColor: '#f0f9f0',
409
+ backgroundColor: '#F0F9F0',
143
410
  },
144
- countryFlag: {
411
+ flag: {
145
412
  fontSize: 32,
146
413
  marginRight: 16,
147
414
  },
148
- countryContent: {
415
+ optionContent: {
149
416
  flex: 1,
150
417
  },
151
- countryName: {
418
+ optionName: {
152
419
  fontSize: 18,
153
420
  fontWeight: '600',
154
421
  color: '#333',
155
422
  },
156
- selectedCountryName: {
423
+ selectedOptionName: {
157
424
  color: '#2DBD60',
158
425
  },
159
- countryCode: {
426
+ optionCode: {
160
427
  fontSize: 14,
161
428
  color: '#666',
162
429
  marginTop: 4,
@@ -174,29 +441,21 @@ const styles = StyleSheet.create({
174
441
  fontSize: 16,
175
442
  fontWeight: 'bold',
176
443
  },
177
- selectionInfo: {
178
- backgroundColor: '#f0f9f0',
179
- padding: 16,
180
- borderRadius: 8,
181
- marginTop: 16,
182
- alignItems: 'center',
183
- },
184
- selectionText: {
185
- fontSize: 16,
186
- fontWeight: '600',
187
- color: '#2DBD60',
444
+ infoBox: {
445
+ backgroundColor: '#F9FAFB',
446
+ padding: 12,
447
+ borderRadius: 12,
448
+ marginBottom: 16,
188
449
  },
189
- requiredContainer: {
190
- backgroundColor: '#fff3cd',
191
- padding: 16,
192
- borderRadius: 8,
193
- marginTop: 16,
194
- alignItems: 'center',
450
+ infoText: {
451
+ fontSize: 14,
452
+ color: '#666',
453
+ lineHeight: 20,
195
454
  },
196
- requiredText: {
197
- fontSize: 16,
198
- fontWeight: '600',
199
- color: '#856404',
455
+ buttonContainer: {
456
+ flexDirection: 'row',
457
+ marginTop: 24,
458
+ gap: 12,
200
459
  },
201
460
  errorText: {
202
461
  color: '#dc2626',