@sanctum-key/react-native-sdk 1.0.48 → 1.0.50
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/build/package.json +1 -1
- package/build/src/components/KYCElements/CountrySelection.d.ts.map +1 -1
- package/build/src/components/KYCElements/CountrySelection.js +33 -108
- package/build/src/components/KYCElements/CountrySelection.js.map +1 -1
- package/build/src/components/KYCElements/CountrySelectionTemplate.d.ts.map +1 -1
- package/build/src/components/KYCElements/CountrySelectionTemplate.js +50 -164
- package/build/src/components/KYCElements/CountrySelectionTemplate.js.map +1 -1
- package/build/src/components/KYCElements/DocumentTypeSelection.d.ts.map +1 -1
- package/build/src/components/KYCElements/DocumentTypeSelection.js +32 -109
- package/build/src/components/KYCElements/DocumentTypeSelection.js.map +1 -1
- package/build/src/components/KYCElements/IDCardCapture.d.ts.map +1 -1
- package/build/src/components/KYCElements/IDCardCapture.js +58 -25
- package/build/src/components/KYCElements/IDCardCapture.js.map +1 -1
- package/build/src/components/KYCElements/InitializationStep.d.ts.map +1 -1
- package/build/src/components/KYCElements/InitializationStep.js +13 -1
- package/build/src/components/KYCElements/InitializationStep.js.map +1 -1
- package/build/src/components/KYCElements/LocationCaptureTemplate.d.ts.map +1 -1
- package/build/src/components/KYCElements/LocationCaptureTemplate.js +46 -157
- package/build/src/components/KYCElements/LocationCaptureTemplate.js.map +1 -1
- package/build/src/components/KYCElements/SelfieCapture.d.ts.map +1 -1
- package/build/src/components/KYCElements/SelfieCapture.js +28 -24
- package/build/src/components/KYCElements/SelfieCapture.js.map +1 -1
- package/build/src/components/KYCElements/VerificationProgressTemplate.d.ts.map +1 -1
- package/build/src/components/KYCElements/VerificationProgressTemplate.js +31 -40
- package/build/src/components/KYCElements/VerificationProgressTemplate.js.map +1 -1
- package/build/src/components/SelfieCapture.d.ts.map +1 -1
- package/build/src/components/SelfieCapture.js +33 -90
- package/build/src/components/SelfieCapture.js.map +1 -1
- package/build/src/components/TemplateKYCExample.d.ts.map +1 -1
- package/build/src/components/TemplateKYCExample.js +69 -120
- package/build/src/components/TemplateKYCExample.js.map +1 -1
- package/build/src/components/TemplateKYCFlowRefactored.d.ts +0 -2
- package/build/src/components/TemplateKYCFlowRefactored.d.ts.map +1 -1
- package/build/src/components/TemplateKYCFlowRefactored.js +95 -210
- package/build/src/components/TemplateKYCFlowRefactored.js.map +1 -1
- package/build/src/hooks/useTemplateKYCFlow.d.ts.map +1 -1
- package/build/src/hooks/useTemplateKYCFlow.js.map +1 -1
- package/build/src/types/KYC.types.d.ts.map +1 -1
- package/build/src/types/KYC.types.js.map +1 -1
- package/build/src/utils/logger.d.ts +12 -4
- package/build/src/utils/logger.d.ts.map +1 -1
- package/build/src/utils/logger.js +156 -28
- package/build/src/utils/logger.js.map +1 -1
- package/package.json +1 -1
- package/src/components/KYCElements/CountrySelection.tsx +36 -109
- package/src/components/KYCElements/CountrySelectionTemplate.tsx +67 -266
- package/src/components/KYCElements/DocumentTypeSelection.tsx +34 -110
- package/src/components/KYCElements/IDCardCapture.tsx +93 -39
- package/src/components/KYCElements/InitializationStep.tsx +17 -2
- package/src/components/KYCElements/LocationCaptureTemplate.tsx +53 -170
- package/src/components/KYCElements/SelfieCapture.tsx +50 -58
- package/src/components/KYCElements/VerificationProgressTemplate.tsx +38 -45
- package/src/components/SelfieCapture.tsx +44 -100
- package/src/components/TemplateKYCExample.tsx +94 -145
- package/src/components/TemplateKYCFlowRefactored.tsx +105 -239
- package/src/hooks/useTemplateKYCFlow.tsx +1 -0
- package/src/types/KYC.types.ts +0 -1
- package/src/utils/logger.ts +182 -29
|
@@ -6,6 +6,7 @@ import { useTemplateKYCFlowContext } from '../../hooks/useTemplateKYCFlow';
|
|
|
6
6
|
import { Button } from '../ui/Button';
|
|
7
7
|
import { countryData } from '../../config/countriesData';
|
|
8
8
|
import REGION_MAPPING from '../../config/region_mapping.json';
|
|
9
|
+
import { sdkLogger } from '../../utils/logger'; // 🚨 IMPORT SDK LOGGER
|
|
9
10
|
|
|
10
11
|
interface CountrySelectionTemplateProps {
|
|
11
12
|
component: TemplateComponent;
|
|
@@ -25,51 +26,23 @@ type ExtendedIDCardConfig = IDCardConfig & {
|
|
|
25
26
|
};
|
|
26
27
|
|
|
27
28
|
const ISO_TO_COUNTRY_NAME: Record<string, string> = { 'KE': 'Kenya', 'CM': 'Cameroon', 'NG': 'Nigeria', 'CA': 'Canada', 'FR': 'France', 'GH': 'Ghana', 'ZA': 'South Africa', 'GB': 'Britain', 'CI': 'Ivory Coast', 'SN': 'Senegal', 'TG': 'Togo', 'ML': 'Mali' };
|
|
29
|
+
const jsonDocMap: Record<string, string> = { 'nationalId': 'identity_card', 'national_id': 'identity_card', 'passport': 'passport', 'driversLicense': 'drivers_licence', 'drivers_licence': 'drivers_licence', 'residencePermit': 'permanent_residence', 'healthInsuranceCard': 'health_insurance_card' };
|
|
30
|
+
const SUB_REGION_CODES: Record<string, string> = { "Alberta": "AB", "British Columbia": "BC", "Manitoba": "MB", "New Brunswick": "NB", "Newfoundland and Labrador": "NL", "Northwest Territories": "NT", "Nova Scotia": "NS", "Nunavut": "NU", "Ontario": "ON", "Prince Edward Island": "PE", "Quebec": "QC", "Saskatchewan": "SK", "Yukon": "YT" };
|
|
28
31
|
|
|
29
|
-
const
|
|
30
|
-
'nationalId': 'identity_card',
|
|
31
|
-
'national_id': 'identity_card',
|
|
32
|
-
'passport': 'passport',
|
|
33
|
-
'driversLicense': 'drivers_licence',
|
|
34
|
-
'drivers_licence': 'drivers_licence',
|
|
35
|
-
'residencePermit': 'permanent_residence',
|
|
36
|
-
'healthInsuranceCard': 'health_insurance_card'
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
const SUB_REGION_CODES: Record<string, string> = {
|
|
40
|
-
"Alberta": "AB",
|
|
41
|
-
"British Columbia": "BC",
|
|
42
|
-
"Manitoba": "MB",
|
|
43
|
-
"New Brunswick": "NB",
|
|
44
|
-
"Newfoundland and Labrador": "NL",
|
|
45
|
-
"Northwest Territories": "NT",
|
|
46
|
-
"Nova Scotia": "NS",
|
|
47
|
-
"Nunavut": "NU",
|
|
48
|
-
"Ontario": "ON",
|
|
49
|
-
"Prince Edward Island": "PE",
|
|
50
|
-
"Quebec": "QC",
|
|
51
|
-
"Saskatchewan": "SK",
|
|
52
|
-
"Yukon": "YT"
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
export const CountrySelectionTemplate: React.FC<CountrySelectionTemplateProps> = ({
|
|
56
|
-
component,
|
|
57
|
-
value,
|
|
58
|
-
onValueChange,
|
|
59
|
-
error,
|
|
60
|
-
language = 'en',
|
|
61
|
-
}) => {
|
|
32
|
+
export const CountrySelectionTemplate: React.FC<CountrySelectionTemplateProps> = ({ component, value, onValueChange, error, language = 'en', }) => {
|
|
62
33
|
const config = component.config as CountrySelectionConfig;
|
|
63
34
|
const { actions, state } = useTemplateKYCFlowContext();
|
|
64
35
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
}, [idCardComponent]);
|
|
36
|
+
// 🚨 LOG: Track when the user reaches this step
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
if (state.session?.session_id) {
|
|
39
|
+
sdkLogger.info("User started country and document selection step");
|
|
40
|
+
}
|
|
41
|
+
}, [state.session?.session_id]);
|
|
72
42
|
|
|
43
|
+
const idCardComponent = useMemo(() => { return state.template.components.find(c => c.type === 'id_card'); }, [state.template.components]);
|
|
44
|
+
const idCardConfig = useMemo(() => { return idCardComponent?.config as ExtendedIDCardConfig | undefined; }, [idCardComponent]);
|
|
45
|
+
|
|
73
46
|
const [currentStep, setCurrentStep] = useState<SelectionStep>(() => {
|
|
74
47
|
if (value?.code && value?.documentType) {
|
|
75
48
|
if (value.region) return 'region';
|
|
@@ -77,11 +50,10 @@ export const CountrySelectionTemplate: React.FC<CountrySelectionTemplateProps> =
|
|
|
77
50
|
}
|
|
78
51
|
return 'country';
|
|
79
52
|
});
|
|
80
|
-
|
|
53
|
+
|
|
81
54
|
const [selectedCountryCode, setSelectedCountryCode] = useState<string | null>(value?.code || null);
|
|
82
55
|
const [selectedDocumentType, setSelectedDocumentType] = useState<string | null>(value?.documentType || null);
|
|
83
56
|
const [selectedRegion, setSelectedRegion] = useState<string | null>(value?.region || null);
|
|
84
|
-
|
|
85
57
|
const [searchQuery, setSearchQuery] = useState('');
|
|
86
58
|
|
|
87
59
|
useEffect(() => {
|
|
@@ -89,28 +61,23 @@ export const CountrySelectionTemplate: React.FC<CountrySelectionTemplateProps> =
|
|
|
89
61
|
setSelectedCountryCode(value.code);
|
|
90
62
|
setSelectedDocumentType(value.documentType);
|
|
91
63
|
setSelectedRegion(value.region || null);
|
|
92
|
-
|
|
93
64
|
if (value.region) {
|
|
94
65
|
setCurrentStep('region');
|
|
95
66
|
} else {
|
|
96
67
|
let regionsCheck = idCardConfig?.regionsByCountry?.[value.code]?.[value.documentType] || [];
|
|
97
|
-
|
|
98
68
|
if (regionsCheck.length === 0) {
|
|
99
69
|
const rawCountryName = ISO_TO_COUNTRY_NAME[value.code] || value.code;
|
|
100
70
|
const searchDocType = jsonDocMap[value.documentType] || value.documentType;
|
|
101
71
|
const baseMapping = (REGION_MAPPING as any).regionMapping || REGION_MAPPING;
|
|
102
|
-
|
|
103
72
|
let countryMapping = baseMapping[rawCountryName];
|
|
104
73
|
if (!countryMapping) {
|
|
105
74
|
const foundKey = Object.keys(baseMapping).find(k => k.toLowerCase() === rawCountryName.toLowerCase());
|
|
106
75
|
if (foundKey) countryMapping = baseMapping[foundKey];
|
|
107
76
|
}
|
|
108
|
-
|
|
109
77
|
if (countryMapping && countryMapping[searchDocType]) {
|
|
110
78
|
regionsCheck = Object.keys(countryMapping[searchDocType]).filter(k => k !== 'root');
|
|
111
79
|
}
|
|
112
80
|
}
|
|
113
|
-
|
|
114
81
|
if (Array.isArray(regionsCheck) && regionsCheck.length > 0) {
|
|
115
82
|
setCurrentStep('region');
|
|
116
83
|
} else {
|
|
@@ -126,16 +93,12 @@ export const CountrySelectionTemplate: React.FC<CountrySelectionTemplateProps> =
|
|
|
126
93
|
}, [value?.code, value?.documentType, value?.region, idCardConfig]);
|
|
127
94
|
|
|
128
95
|
useEffect(() => {
|
|
129
|
-
if (error) {
|
|
130
|
-
actions.updateComponentData(component.id, value);
|
|
131
|
-
}
|
|
96
|
+
if (error) { actions.updateComponentData(component.id, value); }
|
|
132
97
|
}, []);
|
|
133
98
|
|
|
134
99
|
const availableCountries = useMemo(() => {
|
|
135
100
|
const explicitlyAllowed = idCardConfig?.selectedCountries || config.allowed_countries || [];
|
|
136
|
-
|
|
137
101
|
const configuredCodes = new Set(explicitlyAllowed);
|
|
138
|
-
|
|
139
102
|
return Array.from(configuredCodes)
|
|
140
103
|
.filter((code: string) => countryData[code])
|
|
141
104
|
.map((code: string) => ({ code, ...countryData[code] }))
|
|
@@ -145,131 +108,86 @@ export const CountrySelectionTemplate: React.FC<CountrySelectionTemplateProps> =
|
|
|
145
108
|
const filteredCountries = useMemo(() => {
|
|
146
109
|
if (!searchQuery.trim()) return availableCountries;
|
|
147
110
|
const cleanQuery = searchQuery.toLowerCase().trim();
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
country.name.toLowerCase().includes(cleanQuery) ||
|
|
151
|
-
country.name_en.toLowerCase().includes(cleanQuery) ||
|
|
152
|
-
country.code.toLowerCase().includes(cleanQuery)
|
|
111
|
+
return availableCountries.filter(country =>
|
|
112
|
+
country.name.toLowerCase().includes(cleanQuery) || country.name_en.toLowerCase().includes(cleanQuery) || country.code.toLowerCase().includes(cleanQuery)
|
|
153
113
|
);
|
|
154
114
|
}, [searchQuery, availableCountries]);
|
|
155
115
|
|
|
156
116
|
const availableDocumentTypes = useMemo(() => {
|
|
157
117
|
if (!selectedCountryCode) return [];
|
|
158
|
-
|
|
159
|
-
// Check if custom layout configuration explicitly defined items for it
|
|
160
118
|
if (idCardConfig?.documentTypesByCountry?.[selectedCountryCode]) {
|
|
161
119
|
return idCardConfig.documentTypesByCountry[selectedCountryCode];
|
|
162
120
|
}
|
|
163
|
-
|
|
164
|
-
// Default Fallback: Query mapping matrix dynamically
|
|
165
121
|
const rawCountryName = ISO_TO_COUNTRY_NAME[selectedCountryCode] || selectedCountryCode;
|
|
166
122
|
const baseMapping = (REGION_MAPPING as any).regionMapping || REGION_MAPPING;
|
|
167
|
-
|
|
168
123
|
let countryMapping = baseMapping[rawCountryName];
|
|
169
124
|
if (!countryMapping) {
|
|
170
125
|
const foundKey = Object.keys(baseMapping).find(k => k.toLowerCase() === rawCountryName.toLowerCase());
|
|
171
126
|
if (foundKey) countryMapping = baseMapping[foundKey];
|
|
172
127
|
}
|
|
173
|
-
|
|
174
128
|
if (countryMapping) {
|
|
175
|
-
const inverseDocMap: Record<string, string> = {
|
|
176
|
-
'identity_card': 'nationalId',
|
|
177
|
-
'passport': 'passport',
|
|
178
|
-
'drivers_licence': 'driversLicense',
|
|
179
|
-
'permanent_residence': 'residencePermit',
|
|
180
|
-
'health_insurance_card': 'healthInsuranceCard'
|
|
181
|
-
};
|
|
129
|
+
const inverseDocMap: Record<string, string> = { 'identity_card': 'nationalId', 'passport': 'passport', 'drivers_licence': 'driversLicense', 'permanent_residence': 'residencePermit', 'health_insurance_card': 'healthInsuranceCard' };
|
|
182
130
|
return Object.keys(countryMapping).map(key => inverseDocMap[key] || key);
|
|
183
131
|
}
|
|
184
|
-
|
|
185
132
|
return ['nationalId', 'passport', 'driversLicense'];
|
|
186
133
|
}, [selectedCountryCode, idCardConfig?.documentTypesByCountry]);
|
|
187
134
|
|
|
188
135
|
const availableRegions = useMemo(() => {
|
|
189
136
|
if (!selectedCountryCode || !selectedDocumentType) return [];
|
|
190
|
-
|
|
191
137
|
let rawRegions: string[] = [];
|
|
192
|
-
|
|
193
138
|
if (idCardConfig?.regionsByCountry?.[selectedCountryCode]?.[selectedDocumentType]) {
|
|
194
139
|
rawRegions = idCardConfig.regionsByCountry[selectedCountryCode][selectedDocumentType];
|
|
195
140
|
}
|
|
196
|
-
|
|
197
141
|
if (rawRegions.length === 0) {
|
|
198
142
|
const rawCountryName = ISO_TO_COUNTRY_NAME[selectedCountryCode] || selectedCountryCode;
|
|
199
143
|
const searchDocType = jsonDocMap[selectedDocumentType] || selectedDocumentType;
|
|
200
144
|
const baseMapping = (REGION_MAPPING as any).regionMapping || REGION_MAPPING;
|
|
201
|
-
|
|
202
145
|
let countryMapping = baseMapping[rawCountryName];
|
|
203
146
|
if (!countryMapping) {
|
|
204
147
|
const foundKey = Object.keys(baseMapping).find(k => k.toLowerCase() === rawCountryName.toLowerCase());
|
|
205
148
|
if (foundKey) countryMapping = baseMapping[foundKey];
|
|
206
149
|
}
|
|
207
|
-
|
|
208
150
|
if (countryMapping && countryMapping[searchDocType]) {
|
|
209
151
|
rawRegions = Object.keys(countryMapping[searchDocType]).filter(k => k !== 'root');
|
|
210
152
|
}
|
|
211
153
|
}
|
|
212
|
-
|
|
213
154
|
if (!Array.isArray(rawRegions)) return [];
|
|
214
|
-
|
|
215
|
-
return rawRegions.sort((a, b) => a.localeCompare(b)).map(region => ({
|
|
216
|
-
label: region,
|
|
217
|
-
value: SUB_REGION_CODES[region] || region
|
|
218
|
-
}));
|
|
155
|
+
return rawRegions.sort((a, b) => a.localeCompare(b)).map(region => ({ label: region, value: SUB_REGION_CODES[region] || region }));
|
|
219
156
|
}, [selectedCountryCode, selectedDocumentType, idCardConfig?.regionsByCountry]);
|
|
220
157
|
|
|
221
|
-
const needsRegion = useMemo(() => {
|
|
222
|
-
|
|
223
|
-
}, [availableRegions]);
|
|
158
|
+
const needsRegion = useMemo(() => { return availableRegions.length > 0; }, [availableRegions]);
|
|
159
|
+
const getLocalizedText = (text: LocalizedText): string => { return text[state.currentLanguage] || text.en || ''; };
|
|
224
160
|
|
|
225
|
-
const
|
|
226
|
-
|
|
227
|
-
};
|
|
228
|
-
|
|
229
|
-
const handleCountrySelect = (countryCode: string) => {
|
|
230
|
-
setSelectedCountryCode(countryCode);
|
|
231
|
-
setSelectedDocumentType(null);
|
|
232
|
-
setSelectedRegion(null);
|
|
233
|
-
};
|
|
234
|
-
|
|
235
|
-
const handleDocumentTypeSelect = (docType: string) => {
|
|
236
|
-
setSelectedDocumentType(docType);
|
|
237
|
-
setSelectedRegion(null);
|
|
238
|
-
};
|
|
239
|
-
|
|
240
|
-
const handleRegionSelect = (regionValue: string) => {
|
|
241
|
-
setSelectedRegion(regionValue);
|
|
242
|
-
};
|
|
161
|
+
const handleCountrySelect = (countryCode: string) => { setSelectedCountryCode(countryCode); setSelectedDocumentType(null); setSelectedRegion(null); };
|
|
162
|
+
const handleDocumentTypeSelect = (docType: string) => { setSelectedDocumentType(docType); setSelectedRegion(null); };
|
|
163
|
+
const handleRegionSelect = (regionValue: string) => { setSelectedRegion(regionValue); };
|
|
243
164
|
|
|
244
165
|
const finalizeSelection = (countryCode: string, docType: string, region: string | null) => {
|
|
245
166
|
const country = countryData[countryCode];
|
|
246
167
|
if (!country) return;
|
|
168
|
+
|
|
169
|
+
// 🚨 LOG: Final payload successfully selected
|
|
170
|
+
sdkLogger.info("User finalized document configuration", {
|
|
171
|
+
countryCode,
|
|
172
|
+
documentType: docType,
|
|
173
|
+
region: region || 'none'
|
|
174
|
+
});
|
|
247
175
|
|
|
248
176
|
const mapping = countryMapping[countryCode as keyof typeof countryMapping];
|
|
249
177
|
const countryObj: Country & { documentType?: string; region?: string } = {
|
|
250
|
-
code: countryCode,
|
|
251
|
-
...country,
|
|
252
|
-
regionMapping: mapping,
|
|
253
|
-
documentType: docType,
|
|
254
|
-
region: region || undefined,
|
|
178
|
+
code: countryCode, ...country, regionMapping: mapping, documentType: docType, region: region || undefined,
|
|
255
179
|
};
|
|
256
|
-
|
|
257
180
|
onValueChange(countryObj);
|
|
258
|
-
|
|
259
|
-
setTimeout(() => {
|
|
260
|
-
actions.nextComponent(countryObj);
|
|
261
|
-
}, 100);
|
|
181
|
+
setTimeout(() => { actions.nextComponent(countryObj); }, 100);
|
|
262
182
|
};
|
|
263
183
|
|
|
264
184
|
const handleBack = () => {
|
|
185
|
+
sdkLogger.info("User navigated back in document selection", { fromStep: currentStep }); // 🚨 LOG
|
|
186
|
+
|
|
265
187
|
if (currentStep === 'region') {
|
|
266
|
-
setCurrentStep('documentType');
|
|
267
|
-
setSelectedRegion(null);
|
|
188
|
+
setCurrentStep('documentType'); setSelectedRegion(null);
|
|
268
189
|
} else if (currentStep === 'documentType') {
|
|
269
|
-
setCurrentStep('country');
|
|
270
|
-
setSelectedDocumentType(null);
|
|
271
|
-
setSelectedRegion(null);
|
|
272
|
-
setSearchQuery('');
|
|
190
|
+
setCurrentStep('country'); setSelectedDocumentType(null); setSelectedRegion(null); setSearchQuery('');
|
|
273
191
|
} else if (currentStep === 'country') {
|
|
274
192
|
actions.previousComponent();
|
|
275
193
|
}
|
|
@@ -279,68 +197,33 @@ export const CountrySelectionTemplate: React.FC<CountrySelectionTemplateProps> =
|
|
|
279
197
|
if (currentStep === 'country' && selectedCountryCode) {
|
|
280
198
|
setCurrentStep('documentType');
|
|
281
199
|
} else if (currentStep === 'documentType' && selectedDocumentType) {
|
|
282
|
-
if (needsRegion) {
|
|
283
|
-
|
|
284
|
-
} else {
|
|
285
|
-
if (selectedCountryCode) {
|
|
286
|
-
finalizeSelection(selectedCountryCode, selectedDocumentType, null);
|
|
287
|
-
}
|
|
200
|
+
if (needsRegion) { setCurrentStep('region'); } else {
|
|
201
|
+
if (selectedCountryCode) { finalizeSelection(selectedCountryCode, selectedDocumentType, null); }
|
|
288
202
|
}
|
|
289
203
|
} else if (currentStep === 'region' && selectedRegion) {
|
|
290
|
-
if (selectedCountryCode && selectedDocumentType) {
|
|
291
|
-
finalizeSelection(selectedCountryCode, selectedDocumentType, selectedRegion);
|
|
292
|
-
}
|
|
204
|
+
if (selectedCountryCode && selectedDocumentType) { finalizeSelection(selectedCountryCode, selectedDocumentType, selectedRegion); }
|
|
293
205
|
}
|
|
294
206
|
};
|
|
295
207
|
|
|
296
208
|
const renderCountrySelection = () => {
|
|
297
209
|
return (
|
|
298
210
|
<>
|
|
299
|
-
<TextInput
|
|
300
|
-
style={styles.searchBar}
|
|
301
|
-
placeholder={state.currentLanguage === "en" ? "🔍 Search country..." : "🔍 Rechercher un pays..."}
|
|
302
|
-
placeholderTextColor="#94A3B8"
|
|
303
|
-
value={searchQuery}
|
|
304
|
-
onChangeText={setSearchQuery}
|
|
305
|
-
autoCorrect={false}
|
|
306
|
-
/>
|
|
211
|
+
<TextInput style={styles.searchBar} placeholder={state.currentLanguage === "en" ? "🔍 Search country..." : "🔍 Rechercher un pays..."} placeholderTextColor="#94A3B8" value={searchQuery} onChangeText={setSearchQuery} autoCorrect={false} />
|
|
307
212
|
<ScrollView style={styles.listContainer} showsVerticalScrollIndicator={false}>
|
|
308
213
|
{filteredCountries.map((country: Country & { code?: string }) => (
|
|
309
|
-
<TouchableOpacity
|
|
310
|
-
key={country.code}
|
|
311
|
-
style={[
|
|
312
|
-
styles.option,
|
|
313
|
-
selectedCountryCode === country.code && styles.selectedOption
|
|
314
|
-
]}
|
|
315
|
-
onPress={() => handleCountrySelect(country.code!)}
|
|
316
|
-
>
|
|
214
|
+
<TouchableOpacity key={country.code} style={[styles.option, selectedCountryCode === country.code && styles.selectedOption]} onPress={() => handleCountrySelect(country.code!)}>
|
|
317
215
|
<Text style={styles.flag}>{country.flag}</Text>
|
|
318
|
-
|
|
319
216
|
<View style={styles.optionContent}>
|
|
320
|
-
<Text style={[
|
|
321
|
-
styles.optionName,
|
|
322
|
-
selectedCountryCode === country.code && styles.selectedOptionName
|
|
323
|
-
]}>
|
|
324
|
-
{state.currentLanguage === "en" ? country.name_en : country.name}
|
|
325
|
-
</Text>
|
|
217
|
+
<Text style={[styles.optionName, selectedCountryCode === country.code && styles.selectedOptionName]}>{state.currentLanguage === "en" ? country.name_en : country.name}</Text>
|
|
326
218
|
<Text style={styles.optionCode}>{country.code}</Text>
|
|
327
219
|
</View>
|
|
328
|
-
|
|
329
220
|
{selectedCountryCode === country.code && (
|
|
330
|
-
<View style={[
|
|
331
|
-
styles.checkmark,
|
|
332
|
-
{ backgroundColor: component.ui.themeColor as string || '#2DBD60' }
|
|
333
|
-
]}>
|
|
334
|
-
<Text style={styles.checkmarkText}>✓</Text>
|
|
335
|
-
</View>
|
|
221
|
+
<View style={[styles.checkmark, { backgroundColor: component.ui.themeColor as string || '#2DBD60' }]}><Text style={styles.checkmarkText}>✓</Text></View>
|
|
336
222
|
)}
|
|
337
223
|
</TouchableOpacity>
|
|
338
224
|
))}
|
|
339
|
-
|
|
340
225
|
{filteredCountries.length === 0 && (
|
|
341
|
-
<Text style={styles.emptyText}>
|
|
342
|
-
{state.currentLanguage === "en" ? "No matching countries found." : "Aucun pays trouvé."}
|
|
343
|
-
</Text>
|
|
226
|
+
<Text style={styles.emptyText}>{state.currentLanguage === "en" ? "No matching countries found." : "Aucun pays trouvé."}</Text>
|
|
344
227
|
)}
|
|
345
228
|
</ScrollView>
|
|
346
229
|
</>
|
|
@@ -348,10 +231,7 @@ export const CountrySelectionTemplate: React.FC<CountrySelectionTemplateProps> =
|
|
|
348
231
|
};
|
|
349
232
|
|
|
350
233
|
const renderDocumentTypeSelection = () => {
|
|
351
|
-
const instructions = selectedDocumentType && idCardConfig?.instructionsByDocumentType?.[selectedDocumentType]
|
|
352
|
-
? getLocalizedText(idCardConfig.instructionsByDocumentType[selectedDocumentType])
|
|
353
|
-
: null;
|
|
354
|
-
|
|
234
|
+
const instructions = selectedDocumentType && idCardConfig?.instructionsByDocumentType?.[selectedDocumentType] ? getLocalizedText(idCardConfig.instructionsByDocumentType[selectedDocumentType]) : null;
|
|
355
235
|
const documentTypeLabels: Record<string, { en: string; fr: string; icon?: string }> = {
|
|
356
236
|
'nationalId': { en: 'National ID', fr: 'Carte nationale d\'identité', icon: '🏛️' },
|
|
357
237
|
'passport': { en: 'Passport', fr: 'Passeport', icon: '📘' },
|
|
@@ -359,45 +239,22 @@ export const CountrySelectionTemplate: React.FC<CountrySelectionTemplateProps> =
|
|
|
359
239
|
'residencePermit': { en: 'Residence Permit', fr: 'Permis de séjour', icon: '🏠' },
|
|
360
240
|
'healthInsuranceCard': { en: 'Health Insurance Card', fr: 'Carte d\'assurance maladie', icon: '🗳️' },
|
|
361
241
|
};
|
|
362
|
-
|
|
363
242
|
return (
|
|
364
243
|
<>
|
|
365
|
-
{instructions && (
|
|
366
|
-
<View style={styles.infoBox}>
|
|
367
|
-
<Text style={styles.infoText}>{instructions}</Text>
|
|
368
|
-
</View>
|
|
369
|
-
)}
|
|
244
|
+
{instructions && (<View style={styles.infoBox}><Text style={styles.infoText}>{instructions}</Text></View>)}
|
|
370
245
|
<ScrollView style={styles.listContainer} showsVerticalScrollIndicator={false}>
|
|
371
246
|
{availableDocumentTypes.map((docType: string) => {
|
|
372
247
|
const label = documentTypeLabels[docType];
|
|
373
248
|
const displayName = label ? (state.currentLanguage === "en" ? label.en : label.fr) : docType;
|
|
374
249
|
const icon = label?.icon || '📄';
|
|
375
|
-
|
|
376
250
|
return (
|
|
377
|
-
<TouchableOpacity
|
|
378
|
-
key={docType}
|
|
379
|
-
style={[
|
|
380
|
-
styles.option,
|
|
381
|
-
selectedDocumentType === docType && styles.selectedOption
|
|
382
|
-
]}
|
|
383
|
-
onPress={() => handleDocumentTypeSelect(docType)}
|
|
384
|
-
>
|
|
251
|
+
<TouchableOpacity key={docType} style={[styles.option, selectedDocumentType === docType && styles.selectedOption]} onPress={() => handleDocumentTypeSelect(docType)}>
|
|
385
252
|
<Text style={styles.flag}>{icon}</Text>
|
|
386
253
|
<View style={styles.optionContent}>
|
|
387
|
-
<Text style={[
|
|
388
|
-
styles.optionName,
|
|
389
|
-
selectedDocumentType === docType && styles.selectedOptionName
|
|
390
|
-
]}>
|
|
391
|
-
{displayName}
|
|
392
|
-
</Text>
|
|
254
|
+
<Text style={[styles.optionName, selectedDocumentType === docType && styles.selectedOptionName]}>{displayName}</Text>
|
|
393
255
|
</View>
|
|
394
256
|
{selectedDocumentType === docType && (
|
|
395
|
-
<View style={[
|
|
396
|
-
styles.checkmark,
|
|
397
|
-
{ backgroundColor: component.ui.themeColor as string || '#2DBD60' }
|
|
398
|
-
]}>
|
|
399
|
-
<Text style={styles.checkmarkText}>✓</Text>
|
|
400
|
-
</View>
|
|
257
|
+
<View style={[styles.checkmark, { backgroundColor: component.ui.themeColor as string || '#2DBD60' }]}><Text style={styles.checkmarkText}>✓</Text></View>
|
|
401
258
|
)}
|
|
402
259
|
</TouchableOpacity>
|
|
403
260
|
);
|
|
@@ -413,32 +270,13 @@ export const CountrySelectionTemplate: React.FC<CountrySelectionTemplateProps> =
|
|
|
413
270
|
<ScrollView style={styles.listContainer} showsVerticalScrollIndicator={false}>
|
|
414
271
|
{availableRegions.map((regionObj, index: number) => {
|
|
415
272
|
return (
|
|
416
|
-
<TouchableOpacity
|
|
417
|
-
key={index}
|
|
418
|
-
style={[
|
|
419
|
-
styles.option,
|
|
420
|
-
selectedRegion === regionObj.value && styles.selectedOption
|
|
421
|
-
]}
|
|
422
|
-
onPress={() => handleRegionSelect(regionObj.value)}
|
|
423
|
-
>
|
|
273
|
+
<TouchableOpacity key={index} style={[styles.option, selectedRegion === regionObj.value && styles.selectedOption]} onPress={() => handleRegionSelect(regionObj.value)}>
|
|
424
274
|
<View style={styles.optionContent}>
|
|
425
|
-
<Text style={[
|
|
426
|
-
|
|
427
|
-
selectedRegion === regionObj.value && styles.selectedOptionName
|
|
428
|
-
]}>
|
|
429
|
-
{regionObj.label}
|
|
430
|
-
</Text>
|
|
431
|
-
{regionObj.value !== regionObj.label && (
|
|
432
|
-
<Text style={styles.optionCode}>{regionObj.value}</Text>
|
|
433
|
-
)}
|
|
275
|
+
<Text style={[styles.optionName, selectedRegion === regionObj.value && styles.selectedOptionName]}>{regionObj.label}</Text>
|
|
276
|
+
{regionObj.value !== regionObj.label && (<Text style={styles.optionCode}>{regionObj.value}</Text>)}
|
|
434
277
|
</View>
|
|
435
278
|
{selectedRegion === regionObj.value && (
|
|
436
|
-
<View style={[
|
|
437
|
-
styles.checkmark,
|
|
438
|
-
{ backgroundColor: component.ui.themeColor as string || '#2DBD60' }
|
|
439
|
-
]}>
|
|
440
|
-
<Text style={styles.checkmarkText}>✓</Text>
|
|
441
|
-
</View>
|
|
279
|
+
<View style={[styles.checkmark, { backgroundColor: component.ui.themeColor as string || '#2DBD60' }]}><Text style={styles.checkmarkText}>✓</Text></View>
|
|
442
280
|
)}
|
|
443
281
|
</TouchableOpacity>
|
|
444
282
|
);
|
|
@@ -450,31 +288,19 @@ export const CountrySelectionTemplate: React.FC<CountrySelectionTemplateProps> =
|
|
|
450
288
|
|
|
451
289
|
const getStepTitle = (): string => {
|
|
452
290
|
switch (currentStep) {
|
|
453
|
-
case 'country':
|
|
454
|
-
|
|
455
|
-
case '
|
|
456
|
-
|
|
457
|
-
case 'region':
|
|
458
|
-
return state.currentLanguage === "en" ? "Select Province/State" : "Sélectionnez la province/l'état";
|
|
459
|
-
default:
|
|
460
|
-
return '';
|
|
291
|
+
case 'country': return getLocalizedText(component.labels as LocalizedText);
|
|
292
|
+
case 'documentType': return state.currentLanguage === "en" ? "Select Document Type" : "Sélectionnez le type de document";
|
|
293
|
+
case 'region': return state.currentLanguage === "en" ? "Select Province/State" : "Sélectionnez la province/l'état";
|
|
294
|
+
default: return '';
|
|
461
295
|
}
|
|
462
296
|
};
|
|
463
297
|
|
|
464
298
|
const getStepDescription = (): string => {
|
|
465
299
|
switch (currentStep) {
|
|
466
|
-
case 'country':
|
|
467
|
-
|
|
468
|
-
case '
|
|
469
|
-
|
|
470
|
-
? "Choose the type of document you want to use"
|
|
471
|
-
: "Choisissez le type de document que vous souhaitez utiliser";
|
|
472
|
-
case 'region':
|
|
473
|
-
return state.currentLanguage === "en"
|
|
474
|
-
? "Select the issuing province or territory"
|
|
475
|
-
: "Sélectionnez la province ou le territoire de délivrance";
|
|
476
|
-
default:
|
|
477
|
-
return '';
|
|
300
|
+
case 'country': return getLocalizedText(component.instructions as LocalizedText);
|
|
301
|
+
case 'documentType': return state.currentLanguage === "en" ? "Choose the type of document you want to use" : "Choisissez le type de document que vous souhaitez utiliser";
|
|
302
|
+
case 'region': return state.currentLanguage === "en" ? "Select the issuing province or territory" : "Sélectionnez la province ou le territoire de délivrance";
|
|
303
|
+
default: return '';
|
|
478
304
|
}
|
|
479
305
|
};
|
|
480
306
|
|
|
@@ -484,9 +310,7 @@ export const CountrySelectionTemplate: React.FC<CountrySelectionTemplateProps> =
|
|
|
484
310
|
return 3;
|
|
485
311
|
};
|
|
486
312
|
|
|
487
|
-
const getTotalSteps = (): number => {
|
|
488
|
-
return needsRegion ? 3 : 2;
|
|
489
|
-
};
|
|
313
|
+
const getTotalSteps = (): number => { return needsRegion ? 3 : 2; };
|
|
490
314
|
|
|
491
315
|
const canGoNext = (): boolean => {
|
|
492
316
|
if (currentStep === 'country') return selectedCountryCode !== null;
|
|
@@ -499,41 +323,18 @@ export const CountrySelectionTemplate: React.FC<CountrySelectionTemplateProps> =
|
|
|
499
323
|
<View style={styles.root}>
|
|
500
324
|
<View style={styles.container}>
|
|
501
325
|
<View style={styles.progressContainer}>
|
|
502
|
-
<Text style={styles.progressText}>
|
|
503
|
-
{state.currentLanguage === "en"
|
|
504
|
-
? `Step ${getProgressStep()} of ${getTotalSteps()}`
|
|
505
|
-
: `Étape ${getProgressStep()} sur ${getTotalSteps()}`}
|
|
506
|
-
</Text>
|
|
326
|
+
<Text style={styles.progressText}>{state.currentLanguage === "en" ? `Step ${getProgressStep()} of ${getTotalSteps()}` : `Étape ${getProgressStep()} sur ${getTotalSteps()}`}</Text>
|
|
507
327
|
</View>
|
|
508
|
-
|
|
509
328
|
<Text style={styles.title}>{getStepTitle()}</Text>
|
|
510
329
|
<Text style={styles.description}>{getStepDescription()}</Text>
|
|
511
|
-
|
|
512
330
|
{currentStep === 'country' && renderCountrySelection()}
|
|
513
331
|
{currentStep === 'documentType' && renderDocumentTypeSelection()}
|
|
514
332
|
{currentStep === 'region' && renderRegionSelection()}
|
|
515
|
-
|
|
516
333
|
<View style={styles.buttonContainer}>
|
|
517
|
-
<Button
|
|
518
|
-
|
|
519
|
-
onPress={handleBack}
|
|
520
|
-
variant={currentStep === 'country' ? "secondary" : "outline"}
|
|
521
|
-
size="large"
|
|
522
|
-
style={{ flex: 1, marginRight: 8 }}
|
|
523
|
-
/>
|
|
524
|
-
<Button
|
|
525
|
-
title={state.currentLanguage === "en" ? "Next" : "Suivant"}
|
|
526
|
-
onPress={handleNext}
|
|
527
|
-
variant="primary"
|
|
528
|
-
size="large"
|
|
529
|
-
style={{ flex: 1 }}
|
|
530
|
-
disabled={!canGoNext()}
|
|
531
|
-
/>
|
|
334
|
+
<Button title={state.currentLanguage === "en" ? "Back" : "Retour"} onPress={handleBack} variant={currentStep === 'country' ? "secondary" : "outline"} size="large" style={{ flex: 1, marginRight: 8 }} />
|
|
335
|
+
<Button title={state.currentLanguage === "en" ? "Next" : "Suivant"} onPress={handleNext} variant="primary" size="large" style={{ flex: 1 }} disabled={!canGoNext()} />
|
|
532
336
|
</View>
|
|
533
|
-
|
|
534
|
-
{error && (
|
|
535
|
-
<Text style={styles.errorText}>{error}</Text>
|
|
536
|
-
)}
|
|
337
|
+
{error && (<Text style={styles.errorText}>{error}</Text>)}
|
|
537
338
|
</View>
|
|
538
339
|
</View>
|
|
539
340
|
);
|