@sanctum-key/react-native-sdk 1.0.24 → 1.0.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/build/package.json +3 -1
  2. package/build/src/components/EnhancedCameraView.d.ts +7 -1
  3. package/build/src/components/EnhancedCameraView.d.ts.map +1 -1
  4. package/build/src/components/EnhancedCameraView.js +88 -30
  5. package/build/src/components/EnhancedCameraView.js.map +1 -1
  6. package/build/src/components/EnhancedCameraView.web.d.ts +5 -1
  7. package/build/src/components/EnhancedCameraView.web.d.ts.map +1 -1
  8. package/build/src/components/EnhancedCameraView.web.js +80 -295
  9. package/build/src/components/EnhancedCameraView.web.js.map +1 -1
  10. package/build/src/components/KYCElements/CameraCapture.js +1 -1
  11. package/build/src/components/KYCElements/CameraCapture.js.map +1 -1
  12. package/build/src/components/KYCElements/CountrySelection.d.ts.map +1 -1
  13. package/build/src/components/KYCElements/CountrySelection.js +38 -218
  14. package/build/src/components/KYCElements/CountrySelection.js.map +1 -1
  15. package/build/src/components/KYCElements/CountrySelectionTemplate.d.ts.map +1 -1
  16. package/build/src/components/KYCElements/CountrySelectionTemplate.js +5 -17
  17. package/build/src/components/KYCElements/CountrySelectionTemplate.js.map +1 -1
  18. package/build/src/components/KYCElements/IDCardCapture.d.ts.map +1 -1
  19. package/build/src/components/KYCElements/IDCardCapture.js +509 -95
  20. package/build/src/components/KYCElements/IDCardCapture.js.map +1 -1
  21. package/build/src/components/KYCElements/VerificationProgressTemplate.d.ts.map +1 -1
  22. package/build/src/components/KYCElements/VerificationProgressTemplate.js +93 -51
  23. package/build/src/components/KYCElements/VerificationProgressTemplate.js.map +1 -1
  24. package/build/src/components/OverLay/type.d.ts +1 -0
  25. package/build/src/components/OverLay/type.d.ts.map +1 -1
  26. package/build/src/components/OverLay/type.js.map +1 -1
  27. package/build/src/components/WebBarcodeScanner.d.ts +9 -0
  28. package/build/src/components/WebBarcodeScanner.d.ts.map +1 -0
  29. package/build/src/components/WebBarcodeScanner.js +152 -0
  30. package/build/src/components/WebBarcodeScanner.js.map +1 -0
  31. package/build/src/config/KYCConfigs.js +2 -2
  32. package/build/src/config/KYCConfigs.js.map +1 -1
  33. package/build/src/config/region_mapping.json +0 -14
  34. package/build/src/modules/api/CardAuthentification.d.ts +2 -0
  35. package/build/src/modules/api/CardAuthentification.d.ts.map +1 -1
  36. package/build/src/modules/api/CardAuthentification.js +22 -39
  37. package/build/src/modules/api/CardAuthentification.js.map +1 -1
  38. package/build/src/modules/api/KYCService.d.ts +7 -2
  39. package/build/src/modules/api/KYCService.d.ts.map +1 -1
  40. package/build/src/modules/api/KYCService.js +163 -57
  41. package/build/src/modules/api/KYCService.js.map +1 -1
  42. package/build/src/types/KYC.types.d.ts +1 -0
  43. package/build/src/types/KYC.types.d.ts.map +1 -1
  44. package/build/src/types/KYC.types.js.map +1 -1
  45. package/build/src/utils/aamvaParser.d.ts +62 -0
  46. package/build/src/utils/aamvaParser.d.ts.map +1 -0
  47. package/build/src/utils/aamvaParser.js +91 -0
  48. package/build/src/utils/aamvaParser.js.map +1 -0
  49. package/build/src/utils/cropByObb.d.ts.map +1 -1
  50. package/build/src/utils/cropByObb.js +4 -3
  51. package/build/src/utils/cropByObb.js.map +1 -1
  52. package/build/src/web/WebKYCEntry.d.ts +1 -1
  53. package/build/src/web/WebKYCEntry.d.ts.map +1 -1
  54. package/build/src/web/WebKYCEntry.js +105 -239
  55. package/build/src/web/WebKYCEntry.js.map +1 -1
  56. package/package.json +3 -1
  57. package/src/components/EnhancedCameraView.tsx +116 -42
  58. package/src/components/EnhancedCameraView.web.tsx +98 -338
  59. package/src/components/KYCElements/CameraCapture.tsx +1 -1
  60. package/src/components/KYCElements/CountrySelection.tsx +60 -240
  61. package/src/components/KYCElements/CountrySelectionTemplate.tsx +7 -20
  62. package/src/components/KYCElements/IDCardCapture.tsx +672 -151
  63. package/src/components/KYCElements/VerificationProgressTemplate.tsx +112 -65
  64. package/src/components/OverLay/type.ts +1 -1
  65. package/src/components/WebBarcodeScanner.tsx +273 -0
  66. package/src/config/KYCConfigs.ts +2 -2
  67. package/src/config/region_mapping.json +0 -14
  68. package/src/modules/api/CardAuthentification.ts +41 -56
  69. package/src/modules/api/KYCService.ts +236 -82
  70. package/src/types/KYC.types.ts +1 -0
  71. package/src/utils/aamvaParser.ts +102 -0
  72. package/src/utils/cropByObb.ts +5 -3
  73. package/src/web/WebKYCEntry.tsx +125 -301
@@ -1,12 +1,5 @@
1
1
  import React, { useState, useMemo } from 'react';
2
- import {
3
- View,
4
- Text,
5
- TouchableOpacity,
6
- StyleSheet,
7
- FlatList,
8
- TextInput
9
- } from 'react-native';
2
+ import { View, Text, TouchableOpacity, StyleSheet, FlatList, TextInput } from 'react-native';
10
3
  import { KYCElement, KYCElementOption } from '../../types/KYC.types';
11
4
 
12
5
  interface CountrySelectionProps {
@@ -16,255 +9,74 @@ interface CountrySelectionProps {
16
9
  error?: string;
17
10
  }
18
11
 
19
- // 🌍 Comprehensive Global Country List
20
- const ALL_COUNTRIES: KYCElementOption[] = [
21
- { value: 'AF', label: 'Afghanistan', icon: '🇦🇫' },
22
- { value: 'AL', label: 'Albania', icon: '🇦🇱' },
23
- { value: 'DZ', label: 'Algeria', icon: '🇩🇿' },
24
- { value: 'AD', label: 'Andorra', icon: '🇦🇩' },
25
- { value: 'AO', label: 'Angola', icon: '🇦🇴' },
26
- { value: 'AR', label: 'Argentina', icon: '🇦🇷' },
27
- { value: 'AM', label: 'Armenia', icon: '🇦🇲' },
28
- { value: 'AU', label: 'Australia', icon: '🇦🇺' },
29
- { value: 'AT', label: 'Austria', icon: '🇦🇹' },
30
- { value: 'AZ', label: 'Azerbaijan', icon: '🇦🇿' },
31
- { value: 'BS', label: 'Bahamas', icon: '🇧🇸' },
32
- { value: 'BH', label: 'Bahrain', icon: '🇧🇭' },
33
- { value: 'BD', label: 'Bangladesh', icon: '🇧🇩' },
34
- { value: 'BB', label: 'Barbados', icon: '🇧🇧' },
35
- { value: 'BY', label: 'Belarus', icon: '🇧🇾' },
36
- { value: 'BE', label: 'Belgium', icon: '🇧🇪' },
37
- { value: 'BZ', label: 'Belize', icon: '🇧🇿' },
38
- { value: 'BJ', label: 'Benin', icon: '🇧🇯' },
39
- { value: 'BT', label: 'Bhutan', icon: '🇧🇹' },
40
- { value: 'BO', label: 'Bolivia', icon: '🇧🇴' },
41
- { value: 'BA', label: 'Bosnia and Herzegovina', icon: '🇧🇦' },
42
- { value: 'BW', label: 'Botswana', icon: '🇧🇼' },
43
- { value: 'BR', label: 'Brazil', icon: '🇧🇷' },
44
- { value: 'BN', label: 'Brunei', icon: '🇧🇳' },
45
- { value: 'BG', label: 'Bulgaria', icon: '🇧🇬' },
46
- { value: 'BF', label: 'Burkina Faso', icon: '🇧🇫' },
47
- { value: 'BI', label: 'Burundi', icon: '🇧🇮' },
48
- { value: 'CV', label: 'Cabo Verde', icon: '🇨🇻' },
49
- { value: 'KH', label: 'Cambodia', icon: '🇰🇭' },
50
- { value: 'CM', label: 'Cameroon', icon: '🇨🇲' },
51
- { value: 'CA', label: 'Canada', icon: '🇨🇦' },
52
- { value: 'CF', label: 'Central African Republic', icon: '🇨🇫' },
53
- { value: 'TD', label: 'Chad', icon: '🇹🇩' },
54
- { value: 'CL', label: 'Chile', icon: '🇨🇱' },
55
- { value: 'CN', label: 'China', icon: '🇨🇳' },
56
- { value: 'CO', label: 'Colombia', icon: '🇨🇴' },
57
- { value: 'KM', label: 'Comoros', icon: '🇰🇲' },
58
- { value: 'CG', label: 'Congo', icon: '🇨🇬' },
59
- { value: 'CD', label: 'Congo (DRC)', icon: '🇨🇩' },
60
- { value: 'CR', label: 'Costa Rica', icon: '🇨🇷' },
61
- { value: 'HR', label: 'Croatia', icon: '🇭🇷' },
62
- { value: 'CU', label: 'Cuba', icon: '🇨🇺' },
63
- { value: 'CY', label: 'Croatia', icon: '🇨🇾' },
64
- { value: 'CZ', label: 'Czechia', icon: '🇨🇿' },
65
- { value: 'DK', label: 'Denmark', icon: '🇩🇰' },
66
- { value: 'DJ', label: 'Djibouti', icon: '🇩🇯' },
67
- { value: 'DM', label: 'Dominica', icon: '🇩🇲' },
68
- { value: 'DO', label: 'Dominican Republic', icon: '🇩🇴' },
69
- { value: 'EC', label: 'Ecuador', icon: '🇪🇨' },
70
- { value: 'EG', label: 'Egypt', icon: '🇪🇬' },
71
- { value: 'SV', label: 'El Salvador', icon: '🇸🇻' },
72
- { value: 'GQ', label: 'Equatorial Guinea', icon: '🇬🇶' },
73
- { value: 'ER', label: 'Eritrea', icon: '🇪🇷' },
74
- { value: 'EE', label: 'Estonia', icon: '🇪🇪' },
75
- { value: 'SZ', label: 'Eswatini', icon: '🇸🇿' },
76
- { value: 'ET', label: 'Ethiopia', icon: '🇪🇹' },
77
- { value: 'FJ', label: 'Fiji', icon: '🇫🇯' },
78
- { value: 'FI', label: 'Finland', icon: '🇫🇮' },
79
- { value: 'FR', label: 'France', icon: '🇫🇷' },
80
- { value: 'GA', label: 'Gabon', icon: '🇬🇦' },
81
- { value: 'GM', label: 'Gambia', icon: '🇬🇲' },
82
- { value: 'GE', label: 'Georgia', icon: '🇬🇪' },
83
- { value: 'DE', label: 'Germany', icon: '🇩🇪' },
84
- { value: 'GH', label: 'Ghana', icon: '🇬🇭' },
85
- { value: 'GR', label: 'Greece', icon: '🇬🇷' },
86
- { value: 'GD', label: 'Grenada', icon: '🇬🇩' },
87
- { value: 'GT', label: 'Guatemala', icon: '🇬🇹' },
88
- { value: 'GN', label: 'Guinea', icon: '🇬🇳' },
89
- { value: 'GW', label: 'Guinea-Bissau', icon: '🇬🇼' },
90
- { value: 'GY', label: 'Guyana', icon: '🇬🇾' },
91
- { value: 'HT', label: 'Haiti', icon: '🇭🇹' },
92
- { value: 'HN', label: 'Honduras', icon: '🇭🇳' },
93
- { value: 'HU', label: 'Hungary', icon: '🇭🇺' },
94
- { value: 'IS', label: 'Iceland', icon: '🇮🇸' },
95
- { value: 'IN', label: 'India', icon: '🇮🇳' },
96
- { value: 'ID', label: 'Indonesia', icon: '🇮🇩' },
97
- { value: 'IR', label: 'Iran', icon: '🇮🇷' },
98
- { value: 'IQ', label: 'Iraq', icon: '🇮🇶' },
99
- { value: 'IE', label: 'Ireland', icon: '🇮🇪' },
100
- { value: 'IL', label: 'Ireland', icon: '🇮🇪' },
101
- { value: 'IT', label: 'Italy', icon: '🇮🇹' },
102
- { value: 'JM', label: 'Jamaica', icon: '🇯🇲' },
103
- { value: 'JP', label: 'Japan', icon: '🇯🇵' },
104
- { value: 'JO', label: 'Jordan', icon: '🇯🇴' },
105
- { value: 'KZ', label: 'Kazakhstan', icon: '🇰🇿' },
106
- { value: 'KE', label: 'Kenya', icon: '🇰🇪' },
107
- { value: 'KI', label: 'Kiribati', icon: '🇰🇮' },
108
- { value: 'KP', label: 'Korea (North)', icon: '🇰🇵' },
109
- { value: 'KR', label: 'Korea (South)', icon: '🇰🇷' },
110
- { value: 'KW', label: 'Kuwait', icon: '🇰🇼' },
111
- { value: 'KG', label: 'Kyrgyzstan', icon: '🇰🇬' },
112
- { value: 'LA', label: 'Laos', icon: '🇱🇦' },
113
- { value: 'LV', label: 'Latvia', icon: '🇱🇻' },
114
- { value: 'LB', label: 'Lebanon', icon: '🇱🇧' },
115
- { value: 'LS', label: 'Lesotho', icon: '🇱🇸' },
116
- { value: 'LR', label: 'Liberia', icon: '🇱🇷' },
117
- { value: 'LY', label: 'Libya', icon: '🇱🇾' },
118
- { value: 'LI', label: 'Liechtenstein', icon: '🇱🇮' },
119
- { value: 'LT', label: 'Lithuania', icon: '🇱🇹' },
120
- { value: 'LU', label: 'Luxembourg', icon: '🇱🇺' },
121
- { value: 'MG', label: 'Madagascar', icon: '🇲🇬' },
122
- { value: 'MW', label: 'Malawi', icon: '🇲🇼' },
123
- { value: 'MY', label: 'Malaysia', icon: '🇲🇾' },
124
- { value: 'MV', label: 'Maldives', icon: '🇲🇻' },
125
- { value: 'ML', label: 'Mali', icon: '🇲🇱' },
126
- { value: 'MT', label: 'Malta', icon: '🇲🇹' },
127
- { value: 'MH', label: 'Marshall Islands', icon: '🇲🇭' },
128
- { value: 'MR', label: 'Mauritania', icon: '🇲🇷' },
129
- { value: 'MU', label: 'Mauritius', icon: '🇲🇺' },
130
- { value: 'MX', label: 'Mexico', icon: '🇲🇽' },
131
- { value: 'FM', label: 'Micronesia', icon: '🇫🇲' },
132
- { value: 'MD', label: 'Moldova', icon: '🇲🇩' },
133
- { value: 'MC', label: 'Monaco', icon: '🇲🇨' },
134
- { value: 'MN', label: 'Mongolia', icon: '🇲🇳' },
135
- { value: 'ME', label: 'Montenegro', icon: '🇲🇪' },
136
- { value: 'MA', label: 'Morocco', icon: '🇲🇦' },
137
- { value: 'MZ', label: 'Mozambique', icon: '🇲🇿' },
138
- { value: 'MM', label: 'Myanmar', icon: '🇲🇲' },
139
- { value: 'NA', label: 'Namibia', icon: '🇳🇦' },
140
- { value: 'NR', label: 'Nauru', icon: '🇳🇷' },
141
- { value: 'NP', label: 'Nepal', icon: '🇳🇵' },
142
- { value: 'NL', label: 'Netherlands', icon: '🇳🇱' },
143
- { value: 'NZ', label: 'New Zealand', icon: '🇳🇿' },
144
- { value: 'NI', label: 'Nicaragua', icon: '🇳🇮' },
145
- { value: 'NE', label: 'Niger', icon: '🇳🇪' },
146
- { value: 'NG', label: 'Nigeria', icon: '🇳🇬' },
147
- { value: 'MK', label: 'North Macedonia', icon: '🇲🇰' },
148
- { value: 'NO', label: 'Norway', icon: '🇳🇴' },
149
- { value: 'OM', label: 'Oman', icon: '🇴🇲' },
150
- { value: 'PK', label: 'Pakistan', icon: '🇵🇰' },
151
- { value: 'PW', label: 'Palau', icon: '🇵🇼' },
152
- { value: 'PA', label: 'Panama', icon: '🇵🇦' },
153
- { value: 'PG', label: 'Papua New Guinea', icon: '🇵🇬' },
154
- { value: 'PY', label: 'Paraguay', icon: '🇵🇾' },
155
- { value: 'PE', label: 'Peru', icon: '🇵🇪' },
156
- { value: 'PH', label: 'Philippines', icon: '🇵🇭' },
157
- { value: 'PL', label: 'Poland', icon: '🇵🇱' },
158
- { value: 'PT', label: 'Portugal', icon: '🇵🇹' },
159
- { value: 'QA', label: 'Qatar', icon: '🇶🇦' },
160
- { value: 'RO', label: 'Romania', icon: '🇷🇴' },
161
- { value: 'RU', label: 'Russia', icon: '🇷🇺' },
162
- { value: 'RW', label: 'Rwanda', icon: '🇷🇼' },
163
- { value: 'WS', label: 'Samoa', icon: '🇼🇸' },
164
- { value: 'SM', label: 'San Marino', icon: '🇸🇲' },
165
- { value: 'ST', label: 'Sao Tome and Principe', icon: '🇸🇹' },
166
- { value: 'SA', label: 'Saudi Arabia', icon: '🇸🇦' },
167
- { value: 'SN', label: 'Senegal', icon: '🇸🇳' },
168
- { value: 'RS', label: 'Serbia', icon: '🇷🇸' },
169
- { value: 'SC', label: 'Seychelles', icon: '🇸🇨' },
170
- { value: 'SL', label: 'Sierra Leone', icon: '🇸🇱' },
171
- { value: 'SG', label: 'Singapore', icon: '🇸🇬' },
172
- { value: 'SK', label: 'Slovakia', icon: '🇸🇰' },
173
- { value: 'SI', label: 'Slovenia', icon: '🇸🇮' },
174
- { value: 'SB', label: 'Solomon Islands', icon: '🇸🇧' },
175
- { value: 'SO', label: 'Somalia', icon: '🇸🇴' },
176
- { value: 'ZA', label: 'South Africa', icon: '🇿🇦' },
177
- { value: 'ES', label: 'Spain', icon: '🇪🇸' },
178
- { value: 'LK', label: 'Sri Lanka', icon: '🇱🇰' },
179
- { value: 'SD', label: 'Sudan', icon: '🇸🇩' },
180
- { value: 'SR', label: 'Suriname', icon: '🇸🇷' },
181
- { value: 'SE', label: 'Sweden', icon: '🇸🇪' },
182
- { value: 'CH', label: 'Switzerland', icon: '🇨🇭' },
183
- { value: 'SY', label: 'Syria', icon: '🇸🇾' },
184
- { value: 'TW', label: 'Taiwan', icon: '🇹🇼' },
185
- { value: 'TJ', label: 'Tajikistan', icon: '🇹🇯' },
186
- { value: 'TZ', label: 'Tanzania', icon: '🇹🇿' },
187
- { value: 'TH', label: 'Thailand', icon: '🇹🇭' },
188
- { value: 'TL', label: 'Timor-Leste', icon: '🇹🇱' },
189
- { value: 'TG', label: 'Togo', icon: '🇹🇬' },
190
- { value: 'TO', label: 'Tonga', icon: '🇹🇴' },
191
- { value: 'TT', label: 'Trinidad and Tobago', icon: '🇹🇹' },
192
- { value: 'TN', label: 'Tunisia', icon: '🇹🇳' },
193
- { value: 'TR', label: 'Turkey', icon: '🇹🇷' },
194
- { value: 'TM', label: 'Turkmenistan', icon: '🇹🇲' },
195
- { value: 'TV', label: 'Tuvalu', icon: '🇹🇻' },
196
- { value: 'UG', label: 'Uganda', icon: '🇺🇬' },
197
- { value: 'UA', label: 'Ukraine', icon: '🇺🇦' },
198
- { value: 'AE', label: 'United Arab Emirates', icon: '🇦🇪' },
199
- { value: 'GB', label: 'United Kingdom', icon: '🇬🇧' },
200
- { value: 'US', label: 'United States', icon: '🇺🇸' },
201
- { value: 'UY', label: 'Uruguay', icon: '🇺🇾' },
202
- { value: 'UZ', label: 'Uzbekistan', icon: '🇺🇿' },
203
- { value: 'VU', label: 'Vanuatu', icon: '🇻🇺' },
204
- { value: 'VA', label: 'Vatican City', icon: '🇻🇦' },
205
- { value: 'VE', label: 'Venezuela', icon: '🇻🇪' },
206
- { value: 'VN', label: 'Vietnam', icon: '🇻🇳' },
207
- { value: 'YE', label: 'Yemen', icon: '🇾🇪' },
208
- { value: 'ZM', label: 'Zambia', icon: '🇿🇲' },
209
- { value: 'ZW', label: 'Zimbabwe', icon: '🇿🇼' },
210
- { value: 'OTHER', label: 'Other', icon: '🌍' },
211
- ];
212
-
213
12
  export const CountrySelection: React.FC<CountrySelectionProps> = ({
214
13
  element,
215
14
  value,
216
15
  onValueChange,
217
16
  error,
218
17
  }) => {
219
- // Use the massive list if the backend didn't supply specific options
220
- const countries = element.options && element.options.length > 0 ? element.options : ALL_COUNTRIES;
18
+ const countries = element.options || [];
19
+ console.log(`Countries options: ${countries}`)
20
+
221
21
  const [searchQuery, setSearchQuery] = useState('');
222
22
 
223
23
  // Filter countries based on user input
224
24
  const filteredCountries = useMemo(() => {
225
25
  if (!searchQuery.trim()) return countries;
226
26
  return countries.filter(country =>
227
- country.label.toLowerCase().includes(searchQuery.toLowerCase())
27
+ country.label.toLowerCase().includes(searchQuery.toLowerCase())
228
28
  );
229
29
  }, [countries, searchQuery]);
230
30
 
231
31
  // Extract the render logic into a separate function for FlatList performance
232
- const renderCountry = ({ item: country }: { item: KYCElementOption }) => (
233
- <TouchableOpacity
234
- style={[
235
- styles.option,
236
- value === country.value && styles.selectedOption
237
- ]}
238
- onPress={() => onValueChange(country.value)}
239
- activeOpacity={0.7}
240
- >
241
- <Text style={styles.optionIcon}>{country.icon}</Text>
242
- <View style={styles.optionContent}>
243
- <Text style={[
244
- styles.optionLabel,
245
- value === country.value && styles.selectedOptionLabel
246
- ]}>
247
- {country.label}
248
- </Text>
249
- {country.description && (
250
- <Text style={styles.optionDescription}>{country.description}</Text>
251
- )}
252
- </View>
253
- {value === country.value && (
254
- <View style={styles.checkmark}>
255
- <Text style={styles.checkmarkText}>✓</Text>
32
+ const renderCountry = ({ item: country }: { item: KYCElementOption }) => {
33
+ const isSelected = value === country.value;
34
+
35
+ return (
36
+ <TouchableOpacity
37
+ style={[
38
+ styles.option,
39
+ isSelected && styles.selectedOption
40
+ ]}
41
+ onPress={() => onValueChange(country.value)}
42
+ activeOpacity={0.7}
43
+ // Accessibility Enhancements
44
+ accessible={true}
45
+ accessibilityRole="button"
46
+ accessibilityState={{ selected: isSelected }}
47
+ accessibilityLabel={`${country.label}. ${isSelected ? 'Selected' : 'Unselected'}.`}
48
+ accessibilityHint={`Tap to select ${country.label} as your country`}
49
+ >
50
+ <Text style={styles.optionIcon}>{country.icon}</Text>
51
+
52
+ <View style={styles.optionContent}>
53
+ <Text style={[
54
+ styles.optionLabel,
55
+ isSelected && styles.selectedOptionLabel
56
+ ]}>
57
+ {country.label}
58
+ </Text>
59
+ {country.description && (
60
+ <Text style={styles.optionDescription}>{country.description}</Text>
61
+ )}
256
62
  </View>
257
- )}
258
- </TouchableOpacity>
259
- );
63
+
64
+ {isSelected && (
65
+ <View style={styles.checkmark}>
66
+ <Text style={styles.checkmarkText}>✓</Text>
67
+ </View>
68
+ )}
69
+ </TouchableOpacity>
70
+ );
71
+ };
260
72
 
261
73
  return (
262
74
  <View style={styles.container}>
263
75
  <View style={styles.headerContainer}>
264
- <Text style={styles.title}>{element.title}</Text>
76
+ <Text style={styles.title} accessibilityRole="header">{element.title}</Text>
265
77
  <Text style={styles.description}>{element.description}</Text>
266
78
 
267
- {/* Search Bar for better UX */}
79
+ {/* Search Bar */}
268
80
  <TextInput
269
81
  style={styles.searchInput}
270
82
  placeholder="Rechercher un pays..."
@@ -272,6 +84,10 @@ export const CountrySelection: React.FC<CountrySelectionProps> = ({
272
84
  value={searchQuery}
273
85
  onChangeText={setSearchQuery}
274
86
  clearButtonMode="while-editing" // Adds a clear 'x' on iOS
87
+ // Accessibility for Search
88
+ accessibilityRole="search"
89
+ accessibilityLabel="Search countries"
90
+ accessibilityHint="Type to filter the list of configured countries below"
275
91
  />
276
92
  </View>
277
93
 
@@ -279,20 +95,24 @@ export const CountrySelection: React.FC<CountrySelectionProps> = ({
279
95
  data={filteredCountries}
280
96
  keyExtractor={(item) => item.value}
281
97
  renderItem={renderCountry}
282
- showsVerticalScrollIndicator={false}
98
+ showsVerticalScrollIndicator={true} // Enable scroll indicator for better UX visibility
283
99
  contentContainerStyle={styles.listContent}
284
100
  keyboardShouldPersistTaps="handled"
101
+ keyboardDismissMode="on-drag" // 🚨 UX FIX: Hides keyboard naturally when user starts scrolling
285
102
  initialNumToRender={15}
286
103
  maxToRenderPerBatch={20}
287
104
  windowSize={5}
288
105
  removeClippedSubviews={true}
106
+ accessibilityRole="list"
289
107
  ListEmptyComponent={
290
- <Text style={styles.emptyText}>Aucun pays trouvé</Text>
108
+ <Text style={styles.emptyText} accessibilityRole="text">
109
+ Aucun pays configuré ou trouvé.
110
+ </Text>
291
111
  }
292
112
  />
293
113
 
294
114
  {error && (
295
- <Text style={styles.errorText}>{error}</Text>
115
+ <Text style={styles.errorText} accessibilityRole="alert">{error}</Text>
296
116
  )}
297
117
  </View>
298
118
  );
@@ -301,7 +121,7 @@ export const CountrySelection: React.FC<CountrySelectionProps> = ({
301
121
  const styles = StyleSheet.create({
302
122
  container: {
303
123
  flex: 1,
304
- backgroundColor: '#fff',
124
+ backgroundColor: '#fff',
305
125
  },
306
126
  headerContainer: {
307
127
  paddingHorizontal: 16,
@@ -334,7 +154,7 @@ const styles = StyleSheet.create({
334
154
  },
335
155
  listContent: {
336
156
  paddingHorizontal: 16,
337
- paddingBottom: 24,
157
+ paddingBottom: 24,
338
158
  },
339
159
  emptyText: {
340
160
  textAlign: 'center',
@@ -353,7 +173,7 @@ const styles = StyleSheet.create({
353
173
  borderColor: '#e5e5e5',
354
174
  shadowColor: '#000',
355
175
  shadowOffset: { width: 0, height: 2 },
356
- shadowOpacity: 0.05,
176
+ shadowOpacity: 0.05,
357
177
  shadowRadius: 3.84,
358
178
  elevation: 3,
359
179
  },
@@ -82,7 +82,6 @@ export const CountrySelectionTemplate: React.FC<CountrySelectionTemplateProps> =
82
82
  const [selectedDocumentType, setSelectedDocumentType] = useState<string | null>(value?.documentType || null);
83
83
  const [selectedRegion, setSelectedRegion] = useState<string | null>(value?.region || null);
84
84
 
85
- // 🚨 Search state property
86
85
  const [searchQuery, setSearchQuery] = useState('');
87
86
 
88
87
  useEffect(() => {
@@ -132,29 +131,17 @@ export const CountrySelectionTemplate: React.FC<CountrySelectionTemplateProps> =
132
131
  }
133
132
  }, []);
134
133
 
135
- // 🚨 THE FIX: Pull ALL countries from configuration + cross-reference any extra ones found in region_mapping.json
136
134
  const availableCountries = useMemo(() => {
137
- const baseMapping = (REGION_MAPPING as any).regionMapping || REGION_MAPPING;
138
- const jsonCountries = Object.keys(baseMapping);
139
-
140
135
  const explicitlyAllowed = idCardConfig?.selectedCountries || config.allowed_countries || [];
141
136
 
142
- // Combine explicit configs with the entire country dictionary database
143
- const combinedCodes = new Set([
144
- ...explicitlyAllowed,
145
- ...Object.keys(countryData).filter(code => {
146
- const fullCountryName = ISO_TO_COUNTRY_NAME[code];
147
- return jsonCountries.some(jc => jc.toLowerCase() === code.toLowerCase() || (fullCountryName && jc.toLowerCase() === fullCountryName.toLowerCase()));
148
- })
149
- ]);
150
-
151
- return Array.from(combinedCodes)
137
+ const configuredCodes = new Set(explicitlyAllowed);
138
+
139
+ return Array.from(configuredCodes)
152
140
  .filter((code: string) => countryData[code])
153
141
  .map((code: string) => ({ code, ...countryData[code] }))
154
142
  .sort((a: Country, b: Country) => a.name.localeCompare(b.name));
155
143
  }, [idCardConfig?.selectedCountries, config.allowed_countries]);
156
144
 
157
- // 🚨 Filter countries list in real-time based on the search query input
158
145
  const filteredCountries = useMemo(() => {
159
146
  if (!searchQuery.trim()) return availableCountries;
160
147
  const cleanQuery = searchQuery.toLowerCase().trim();
@@ -282,7 +269,7 @@ export const CountrySelectionTemplate: React.FC<CountrySelectionTemplateProps> =
282
269
  setCurrentStep('country');
283
270
  setSelectedDocumentType(null);
284
271
  setSelectedRegion(null);
285
- setSearchQuery(''); // Reset search when backing up
272
+ setSearchQuery('');
286
273
  } else if (currentStep === 'country') {
287
274
  actions.previousComponent();
288
275
  }
@@ -309,8 +296,7 @@ export const CountrySelectionTemplate: React.FC<CountrySelectionTemplateProps> =
309
296
  const renderCountrySelection = () => {
310
297
  return (
311
298
  <>
312
- {/* 🔍 Dynamic Search Bar Component */}
313
- <TextInput
299
+ <TextInput
314
300
  style={styles.searchBar}
315
301
  placeholder={state.currentLanguage === "en" ? "🔍 Search country..." : "🔍 Rechercher un pays..."}
316
302
  placeholderTextColor="#94A3B8"
@@ -318,7 +304,6 @@ export const CountrySelectionTemplate: React.FC<CountrySelectionTemplateProps> =
318
304
  onChangeText={setSearchQuery}
319
305
  autoCorrect={false}
320
306
  />
321
-
322
307
  <ScrollView style={styles.listContainer} showsVerticalScrollIndicator={false}>
323
308
  {filteredCountries.map((country: Country & { code?: string }) => (
324
309
  <TouchableOpacity
@@ -330,6 +315,7 @@ export const CountrySelectionTemplate: React.FC<CountrySelectionTemplateProps> =
330
315
  onPress={() => handleCountrySelect(country.code!)}
331
316
  >
332
317
  <Text style={styles.flag}>{country.flag}</Text>
318
+
333
319
  <View style={styles.optionContent}>
334
320
  <Text style={[
335
321
  styles.optionName,
@@ -339,6 +325,7 @@ export const CountrySelectionTemplate: React.FC<CountrySelectionTemplateProps> =
339
325
  </Text>
340
326
  <Text style={styles.optionCode}>{country.code}</Text>
341
327
  </View>
328
+
342
329
  {selectedCountryCode === country.code && (
343
330
  <View style={[
344
331
  styles.checkmark,