@transfergratis/react-native-sdk 0.1.25 → 0.1.26

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 (88) hide show
  1. package/android/src/main/AndroidManifest.xml +12 -0
  2. package/build/components/EnhancedCameraView.web.d.ts.map +1 -1
  3. package/build/components/EnhancedCameraView.web.js +76 -21
  4. package/build/components/EnhancedCameraView.web.js.map +1 -1
  5. package/build/components/KYCElements/EmailVerificationTemplate.d.ts.map +1 -1
  6. package/build/components/KYCElements/EmailVerificationTemplate.js +48 -29
  7. package/build/components/KYCElements/EmailVerificationTemplate.js.map +1 -1
  8. package/build/components/KYCElements/IDCardCapture.d.ts.map +1 -1
  9. package/build/components/KYCElements/IDCardCapture.js +40 -11
  10. package/build/components/KYCElements/IDCardCapture.js.map +1 -1
  11. package/build/components/KYCElements/WelcomeTemplate.js +2 -1
  12. package/build/components/KYCElements/WelcomeTemplate.js.map +1 -1
  13. package/build/components/OverLay/type.d.ts +2 -0
  14. package/build/components/OverLay/type.d.ts.map +1 -1
  15. package/build/components/OverLay/type.js.map +1 -1
  16. package/build/components/TemplateKYCExample.d.ts +8 -2
  17. package/build/components/TemplateKYCExample.d.ts.map +1 -1
  18. package/build/components/TemplateKYCExample.js +2 -2
  19. package/build/components/TemplateKYCExample.js.map +1 -1
  20. package/build/components/TemplateKYCFlowRefactored.d.ts +10 -2
  21. package/build/components/TemplateKYCFlowRefactored.d.ts.map +1 -1
  22. package/build/components/TemplateKYCFlowRefactored.js +13 -3
  23. package/build/components/TemplateKYCFlowRefactored.js.map +1 -1
  24. package/build/config/KYCConfig.js +1 -1
  25. package/build/config/KYCConfig.js.map +1 -1
  26. package/build/hooks/useTemplateKYCFlow.d.ts +14 -2
  27. package/build/hooks/useTemplateKYCFlow.d.ts.map +1 -1
  28. package/build/hooks/useTemplateKYCFlow.js +175 -84
  29. package/build/hooks/useTemplateKYCFlow.js.map +1 -1
  30. package/build/i18n/en/index.d.ts +2 -0
  31. package/build/i18n/en/index.d.ts.map +1 -1
  32. package/build/i18n/en/index.js +3 -1
  33. package/build/i18n/en/index.js.map +1 -1
  34. package/build/i18n/fr/index.d.ts +2 -0
  35. package/build/i18n/fr/index.d.ts.map +1 -1
  36. package/build/i18n/fr/index.js +3 -1
  37. package/build/i18n/fr/index.js.map +1 -1
  38. package/build/i18n/types.d.ts +2 -0
  39. package/build/i18n/types.d.ts.map +1 -1
  40. package/build/i18n/types.js.map +1 -1
  41. package/build/modules/api/CardAuthentification.d.ts.map +1 -1
  42. package/build/modules/api/CardAuthentification.js +22 -2
  43. package/build/modules/api/CardAuthentification.js.map +1 -1
  44. package/build/modules/api/KYCService.d.ts +10 -0
  45. package/build/modules/api/KYCService.d.ts.map +1 -1
  46. package/build/modules/api/KYCService.js +24 -0
  47. package/build/modules/api/KYCService.js.map +1 -1
  48. package/build/modules/camera/VisionCameraModule.web.d.ts.map +1 -1
  49. package/build/modules/camera/VisionCameraModule.web.js +27 -8
  50. package/build/modules/camera/VisionCameraModule.web.js.map +1 -1
  51. package/build/types/KYC.types.d.ts +6 -2
  52. package/build/types/KYC.types.d.ts.map +1 -1
  53. package/build/types/KYC.types.js.map +1 -1
  54. package/build/utils/cropByObb.d.ts +7 -0
  55. package/build/utils/cropByObb.d.ts.map +1 -1
  56. package/build/utils/cropByObb.js +20 -1
  57. package/build/utils/cropByObb.js.map +1 -1
  58. package/build/web/WebKYCEntry.d.ts.map +1 -1
  59. package/build/web/WebKYCEntry.js +11 -5
  60. package/build/web/WebKYCEntry.js.map +1 -1
  61. package/package.json +1 -1
  62. package/plugin/build/index.d.ts +1 -0
  63. package/plugin/build/index.js +3 -1
  64. package/plugin/build/withRemovePermissions.d.ts +3 -0
  65. package/plugin/build/withRemovePermissions.js +67 -0
  66. package/plugin/src/index.ts +2 -1
  67. package/plugin/src/withRemovePermissions.js +85 -0
  68. package/plugin/src/withRemovePermissions.ts +83 -0
  69. package/plugin/tsconfig.tsbuildinfo +1 -1
  70. package/plugin.js +6 -1
  71. package/src/components/EnhancedCameraView.web.tsx +76 -21
  72. package/src/components/KYCElements/EmailVerificationTemplate.tsx +47 -33
  73. package/src/components/KYCElements/IDCardCapture.tsx +41 -10
  74. package/src/components/KYCElements/WelcomeTemplate.tsx +2 -1
  75. package/src/components/OverLay/type.ts +2 -0
  76. package/src/components/TemplateKYCExample.tsx +9 -5
  77. package/src/components/TemplateKYCFlowRefactored.tsx +24 -6
  78. package/src/config/KYCConfig.ts +1 -1
  79. package/src/hooks/useTemplateKYCFlow.tsx +189 -95
  80. package/src/i18n/en/index.ts +3 -1
  81. package/src/i18n/fr/index.ts +3 -1
  82. package/src/i18n/types.ts +2 -0
  83. package/src/modules/api/CardAuthentification.ts +23 -2
  84. package/src/modules/api/KYCService.ts +41 -0
  85. package/src/modules/camera/VisionCameraModule.web.ts +30 -12
  86. package/src/types/KYC.types.ts +7 -3
  87. package/src/utils/cropByObb.ts +20 -1
  88. package/src/web/WebKYCEntry.tsx +17 -6
@@ -2,9 +2,11 @@ import React, { useState, useCallback, useMemo, createContext, useContext, useEf
2
2
  import kycService, { authentification, truncateFields } from '../modules/api/KYCService';
3
3
  import useI18n from './useI18n';
4
4
  import { logger } from '../utils/logger';
5
+ import { countryMapping } from '../config/region_mapping';
6
+ import { countryData } from '../config/countriesData';
5
7
  const TemplateKYCFlowContext = createContext(undefined);
6
- export const TemplateKYCFlowProvider = ({ children, template, onComplete, onError, onCancel, initialLanguage = 'en', apiKey, env = 'PRODUCTION', existingSessionId, }) => {
7
- const hookResult = useTemplateKYCFlow(template, onComplete, onError, onCancel, initialLanguage, apiKey, env, existingSessionId);
8
+ export const TemplateKYCFlowProvider = ({ children, template, onComplete, onError, onCancel, initialLanguage = 'en', apiKey, env = 'PRODUCTION', existingSessionId, initialComponentIndex, initialCountryResume, }) => {
9
+ const hookResult = useTemplateKYCFlow(template, onComplete, onError, onCancel, initialLanguage, apiKey, env, existingSessionId, initialComponentIndex, initialCountryResume);
8
10
  return (<TemplateKYCFlowContext.Provider value={hookResult}>
9
11
  {children}
10
12
  </TemplateKYCFlowContext.Provider>);
@@ -17,7 +19,7 @@ export const useTemplateKYCFlowContext = () => {
17
19
  }
18
20
  return context;
19
21
  };
20
- export const useTemplateKYCFlow = (template, onComplete, onError, onCancel, initialLanguage = 'en', apiKey, env = 'PRODUCTION', existingSessionId, initialStep) => {
22
+ export const useTemplateKYCFlow = (template, onComplete, onError, onCancel, initialLanguage = 'en', apiKey, env = 'PRODUCTION', existingSessionId, initialComponentIndex, initialCountryResume) => {
21
23
  const { setLocale } = useI18n();
22
24
  useEffect(() => {
23
25
  setLocale(initialLanguage);
@@ -70,44 +72,39 @@ export const useTemplateKYCFlow = (template, onComplete, onError, onCancel, init
70
72
  }, [apiKey]);
71
73
  const templateWithReview = useMemo(() => ensureReviewSubmitStep(template), [template, ensureReviewSubmitStep, apiKey]);
72
74
  const templateWithReviewAndVerification = useMemo(() => ensureVerificationProgressStep(templateWithReview), [templateWithReview, ensureVerificationProgressStep, apiKey]);
73
- // État initial du flux
75
+ // État initial du flux (initialComponentIndex = index dans template.components pour reprendre au bon composant)
74
76
  const buildInitialState = () => {
75
- // Valider initialStep pour s'assurer qu'il est dans les limites du template
76
- let validInitialStep = 0;
77
+ let resumeAtIndex = 0;
77
78
  let completedComponents = [];
78
- logger.log('buildInitialState called', { initialStep, existingSessionId });
79
- if (initialStep !== undefined && initialStep >= 0) {
79
+ let initialComponentData = {};
80
+ logger.log('buildInitialState called', { initialComponentIndex, existingSessionId, initialCountryResume });
81
+ if (initialComponentIndex !== undefined && initialComponentIndex >= 0) {
80
82
  const maxIndex = templateWithReviewAndVerification.components.length - 1;
81
- const requestedStep = Math.min(initialStep, maxIndex);
82
- const requestedComponent = templateWithReviewAndVerification.components[requestedStep];
83
- logger.log('Processing initialStep', {
84
- initialStep,
85
- requestedStep,
83
+ const requestedIndex = Math.min(initialComponentIndex, maxIndex);
84
+ const requestedComponent = templateWithReviewAndVerification.components[requestedIndex];
85
+ logger.log('Processing initialComponentIndex (component in template)', {
86
+ initialComponentIndex,
87
+ requestedIndex,
86
88
  maxIndex,
87
89
  componentType: requestedComponent?.type,
88
- componentId: requestedComponent?.id
90
+ componentId: requestedComponent?.id,
89
91
  });
90
- // Si on reprend à l'étape id_card, on peut rester à id_card si on a une session existante
91
- // car les données de country_selection peuvent être chargées depuis la session
92
+ // Reprendre au composant en cours (id_card, selfie, etc.)
92
93
  if (requestedComponent?.type === 'id_card') {
93
- // Si on a une session existante, on peut rester à id_card et charger les données
94
94
  if (existingSessionId) {
95
95
  logger.log('id_card with existing session - staying at id_card');
96
- validInitialStep = requestedStep;
97
- // Marquer les composants précédents comme complétés
98
- if (validInitialStep > 0) {
96
+ resumeAtIndex = requestedIndex;
97
+ if (resumeAtIndex > 0) {
99
98
  completedComponents = templateWithReviewAndVerification.components
100
- .slice(0, validInitialStep)
99
+ .slice(0, resumeAtIndex)
101
100
  .map(component => component.id);
102
101
  }
103
102
  }
104
103
  else {
105
- // Si pas de session, revenir à country_selection pour refaire le choix
106
104
  const countrySelectionIndex = templateWithReviewAndVerification.components.findIndex(c => c.type === 'country_selection');
107
105
  logger.log('id_card without session - going back to country_selection', { countrySelectionIndex });
108
106
  if (countrySelectionIndex >= 0) {
109
- validInitialStep = countrySelectionIndex;
110
- // Marquer les composants avant country_selection comme complétés
107
+ resumeAtIndex = countrySelectionIndex;
111
108
  if (countrySelectionIndex > 0) {
112
109
  completedComponents = templateWithReviewAndVerification.components
113
110
  .slice(0, countrySelectionIndex)
@@ -115,40 +112,45 @@ export const useTemplateKYCFlow = (template, onComplete, onError, onCancel, init
115
112
  }
116
113
  }
117
114
  else {
118
- // Si pas de country_selection, commencer au début
119
- validInitialStep = 0;
115
+ resumeAtIndex = 0;
120
116
  }
121
117
  }
122
118
  }
123
119
  else if (requestedComponent?.type === 'review_submit') {
124
- // Si on reprend au review_submit, on ne marque pas les composants précédents
125
- // pour permettre à l'utilisateur de revenir en arrière et vérifier/modifier les données
126
- validInitialStep = requestedStep;
127
- // Ne pas marquer les composants précédents comme complétés
120
+ resumeAtIndex = requestedIndex;
128
121
  completedComponents = [];
129
122
  }
130
123
  else {
131
- // Pour les autres composants (selfie, etc.), commencer directement à l'étape demandée
132
- validInitialStep = requestedStep;
133
- // Marquer tous les composants précédents comme complétés
134
- // Cela permet à l'utilisateur de continuer sans refaire les étapes précédentes
135
- if (validInitialStep > 0) {
124
+ resumeAtIndex = requestedIndex;
125
+ if (resumeAtIndex > 0) {
136
126
  completedComponents = templateWithReviewAndVerification.components
137
- .slice(0, validInitialStep)
127
+ .slice(0, resumeAtIndex)
138
128
  .map(component => component.id);
139
129
  }
140
130
  }
141
- logger.log('Final initial state', {
142
- validInitialStep,
131
+ logger.log('Final initial state (resume at component index)', {
132
+ resumeAtIndex,
143
133
  completedComponentsCount: completedComponents.length,
144
- componentAtStep: templateWithReviewAndVerification.components[validInitialStep]?.type
134
+ componentAtResume: templateWithReviewAndVerification.components[resumeAtIndex]?.type,
145
135
  });
146
136
  }
137
+ if (initialCountryResume?.code && initialCountryResume?.documentType) {
138
+ const countrySel = templateWithReviewAndVerification.components.find(c => c.type === 'country_selection');
139
+ if (countrySel && countryData[initialCountryResume.code]) {
140
+ initialComponentData[countrySel.id] = {
141
+ code: initialCountryResume.code,
142
+ documentType: initialCountryResume.documentType,
143
+ region: initialCountryResume.region || 'root',
144
+ ...countryData[initialCountryResume.code],
145
+ };
146
+ logger.log('Prefilled country_selection from URL', { code: initialCountryResume.code, documentType: initialCountryResume.documentType });
147
+ }
148
+ }
147
149
  return {
148
150
  template: templateWithReviewAndVerification,
149
- currentComponentIndex: validInitialStep,
151
+ currentComponentIndex: resumeAtIndex,
150
152
  completedComponents: completedComponents,
151
- componentData: {},
153
+ componentData: initialComponentData,
152
154
  errors: {},
153
155
  isProcessing: false,
154
156
  currentLanguage: initialLanguage,
@@ -159,6 +161,7 @@ export const useTemplateKYCFlow = (template, onComplete, onError, onCancel, init
159
161
  isInitialized: false,
160
162
  isProcessing: false,
161
163
  error: null,
164
+ sessionDataRestored: !existingSessionId || Boolean(initialCountryResume?.code && initialCountryResume?.documentType),
162
165
  },
163
166
  verification: {
164
167
  status: 'idle',
@@ -184,9 +187,9 @@ export const useTemplateKYCFlow = (template, onComplete, onError, onCancel, init
184
187
  logger.log('No existingSessionId, skipping data load');
185
188
  return;
186
189
  }
187
- // Si initialStep n'est pas défini ou est 0, on ne charge pas (début de session)
188
- if (initialStep === undefined || initialStep === 0) {
189
- logger.log('initialStep is 0 or undefined, skipping data load');
190
+ // Si initialComponentIndex n'est pas défini ou est 0, on ne charge pas (début de session)
191
+ if (initialComponentIndex === undefined || initialComponentIndex === 0) {
192
+ logger.log('initialComponentIndex is 0 or undefined, skipping data load');
190
193
  return;
191
194
  }
192
195
  // Attendre que la session soit initialisée
@@ -198,7 +201,7 @@ export const useTemplateKYCFlow = (template, onComplete, onError, onCancel, init
198
201
  return;
199
202
  }
200
203
  try {
201
- logger.log('Loading session data for resume:', { sessionId: existingSessionId, step: initialStep });
204
+ logger.log('Loading session data for resume:', { sessionId: existingSessionId, componentIndex: initialComponentIndex });
202
205
  const result = await kycService.getVerificationResult(state.session.session_id);
203
206
  const sessionData = result[state.session.session_id]?.data;
204
207
  if (sessionData) {
@@ -206,10 +209,9 @@ export const useTemplateKYCFlow = (template, onComplete, onError, onCancel, init
206
209
  // Utiliser 'as any' car VerificationResult peut avoir des propriétés dynamiques
207
210
  const data = sessionData;
208
211
  const restoredComponentData = {};
209
- // Parcourir les composants jusqu'à l'étape initiale (incluse) pour restaurer leurs données
210
- // Utiliser initialStep + 1 pour inclure le composant à l'étape initialStep
212
+ // Parcourir les composants jusqu'au composant de reprise (inclu) pour restaurer leurs données
211
213
  templateWithReviewAndVerification.components
212
- .slice(0, initialStep + 1)
214
+ .slice(0, initialComponentIndex + 1)
213
215
  .forEach((component) => {
214
216
  // Essayer de restaurer les données selon le type de composant
215
217
  if (component.type === 'id_card' || component.type === 'file_upload') {
@@ -292,8 +294,29 @@ export const useTemplateKYCFlow = (template, onComplete, onError, onCancel, init
292
294
  }
293
295
  }
294
296
  else if (component.type === 'country_selection') {
295
- // Les données de sélection de pays peuvent être dans metadata
296
- if (data.metadata || data.user_data) {
297
+ // Reconstruire country_selection au format attendu par IDCardCapture / CountrySelectionTemplate (code, documentType, region, regionMapping)
298
+ const meta = data.metadata || data.user_data || data;
299
+ const code = meta.country || meta.country_code || meta.code;
300
+ const documentType = meta.document_type || meta.documentType;
301
+ const region = meta.region;
302
+ if (code && documentType != null) {
303
+ const country = countryData[code];
304
+ const mapping = countryMapping[code];
305
+ if (country) {
306
+ restoredComponentData[component.id] = {
307
+ code,
308
+ ...country,
309
+ documentType,
310
+ region: region || undefined,
311
+ regionMapping: mapping || undefined,
312
+ };
313
+ logger.log('Restored country_selection for resume', { code, documentType, region });
314
+ }
315
+ else {
316
+ restoredComponentData[component.id] = { code, documentType, region: region || undefined, regionMapping: mapping || undefined };
317
+ }
318
+ }
319
+ else if (data.metadata || data.user_data) {
297
320
  restoredComponentData[component.id] = {
298
321
  ...(data.metadata || {}),
299
322
  ...(data.user_data || {}),
@@ -310,6 +333,29 @@ export const useTemplateKYCFlow = (template, onComplete, onError, onCancel, init
310
333
  }
311
334
  }
312
335
  });
336
+ // Fallback: si pas de country_selection restauré mais on a des documents avec templatePath, déduire code/documentType (ex. "templates/national_id_CM_front.jpg")
337
+ const countrySelectionComponent = templateWithReviewAndVerification.components.find(c => c.type === 'country_selection');
338
+ if (countrySelectionComponent && !restoredComponentData[countrySelectionComponent.id] && data.documents) {
339
+ const docs = data.documents;
340
+ const firstDoc = Object.values(docs).find(d => d?.templatePath);
341
+ const path = firstDoc?.templatePath || '';
342
+ const match = path.match(/([a-z_]+)_([A-Z]{2})(?:_|$)/i) || path.match(/([A-Z]{2})/);
343
+ const code = match ? (match[2] || match[1]).toUpperCase().slice(0, 2) : null;
344
+ const docTypeFromPath = path.match(/(national_id|identity_card|passport|passport_card)/i)?.[1]?.toLowerCase().replace('identity_card', 'national_id') || null;
345
+ if (code && countryData[code]) {
346
+ const documentType = docTypeFromPath || 'national_id';
347
+ const mapping = countryMapping[code];
348
+ const country = countryData[code];
349
+ restoredComponentData[countrySelectionComponent.id] = {
350
+ code,
351
+ ...country,
352
+ documentType,
353
+ region: undefined,
354
+ regionMapping: mapping || undefined,
355
+ };
356
+ logger.log('Restored country_selection from document templatePath', { code, documentType, path });
357
+ }
358
+ }
313
359
  // Mettre à jour l'état avec les données restaurées
314
360
  if (Object.keys(restoredComponentData).length > 0) {
315
361
  logger.log('Session data restored - components:', Object.keys(restoredComponentData));
@@ -320,21 +366,42 @@ export const useTemplateKYCFlow = (template, onComplete, onError, onCancel, init
320
366
  ...prev.componentData,
321
367
  ...restoredComponentData,
322
368
  },
369
+ session: { ...prev.session, sessionDataRestored: true },
323
370
  }));
324
371
  logger.log('Component data updated in state');
325
372
  }
326
373
  else {
327
374
  logger.log('No component data to restore from session');
375
+ setState(prev => ({ ...prev, session: { ...prev.session, sessionDataRestored: true } }));
328
376
  }
329
377
  }
378
+ else {
379
+ setState(prev => ({ ...prev, session: { ...prev.session, sessionDataRestored: true } }));
380
+ }
330
381
  }
331
382
  catch (error) {
332
383
  logger.error('Error loading session data:', truncateFields(error));
333
- // Ne pas bloquer le flux si le chargement échoue
384
+ setState(prev => ({ ...prev, session: { ...prev.session, sessionDataRestored: true } }));
334
385
  }
335
386
  };
336
387
  loadSessionData();
337
- }, [existingSessionId, initialStep, state.session.isInitialized, state.session.session_id, templateWithReviewAndVerification.components, base64ToDataUri]);
388
+ }, [existingSessionId, initialComponentIndex, state.session.isInitialized, state.session.session_id, templateWithReviewAndVerification.components, base64ToDataUri]);
389
+ // Si l'index pointe vers Review alors que des étapes ne sont pas complétées, ramener à la première étape incomplète
390
+ useEffect(() => {
391
+ const comp = state.template.components[state.currentComponentIndex];
392
+ if (!comp || comp.type !== 'review_submit')
393
+ return;
394
+ const nonReview = state.template.components.filter(c => c.type !== 'review_submit' && c.type !== 'verification_progress');
395
+ if (nonReview.every(c => state.completedComponents.includes(c.id)))
396
+ return;
397
+ const firstIncomplete = nonReview.find(c => !state.completedComponents.includes(c.id));
398
+ if (!firstIncomplete)
399
+ return;
400
+ const targetIndex = state.template.components.findIndex(c => c.id === firstIncomplete.id);
401
+ if (targetIndex >= 0 && targetIndex !== state.currentComponentIndex) {
402
+ setState(prev => ({ ...prev, currentComponentIndex: targetIndex }));
403
+ }
404
+ }, [state.currentComponentIndex, state.completedComponents, state.template.components]);
338
405
  const mapComponentTypeToAction = useCallback((type) => {
339
406
  switch (type) {
340
407
  case 'id_card':
@@ -386,7 +453,7 @@ export const useTemplateKYCFlow = (template, onComplete, onError, onCancel, init
386
453
  if (!action) {
387
454
  return base;
388
455
  }
389
- // Document upload expects an array of documents with base64 and metadata
456
+ // Document upload expects documents; include country_selection in metadata so resume can restore it
390
457
  if (action === 'document_upload') {
391
458
  const documents = {};
392
459
  if (rawData && typeof rawData === 'object') {
@@ -394,9 +461,19 @@ export const useTemplateKYCFlow = (template, onComplete, onError, onCancel, init
394
461
  documents[key] = rawData[key];
395
462
  });
396
463
  }
464
+ const countryComp = state.template.components.find(c => c.type === 'country_selection');
465
+ const countryDataForPayload = countryComp ? state.componentData[countryComp.id] : null;
466
+ const metadata = {};
467
+ if (countryDataForPayload?.code) {
468
+ metadata.country = countryDataForPayload.code;
469
+ metadata.document_type = countryDataForPayload.documentType;
470
+ if (countryDataForPayload.region != null)
471
+ metadata.region = countryDataForPayload.region;
472
+ }
397
473
  return {
398
474
  ...base,
399
475
  documents,
476
+ ...(Object.keys(metadata).length > 0 ? { metadata } : {}),
400
477
  };
401
478
  }
402
479
  if (action === 'selfie_capture') {
@@ -424,16 +501,28 @@ export const useTemplateKYCFlow = (template, onComplete, onError, onCancel, init
424
501
  }, [state.componentData, apiKey]);
425
502
  // Ensure the template contains a final review step
426
503
  // console.log('apiKey in useTemplateKYCFlow', apiKey);
427
- // Composant actuel
504
+ // Composant actuel (peut être redirigé si on pointe vers Review alors que le flux n'est pas terminé)
428
505
  const currentComponent = useMemo(() => {
429
- return state.template.components[state.currentComponentIndex] || null;
430
- }, [state.template.components, state.currentComponentIndex, apiKey]);
431
- // Progression du flux
506
+ const comp = state.template.components[state.currentComponentIndex] || null;
507
+ if (!comp || comp.type !== 'review_submit')
508
+ return comp;
509
+ const nonReview = state.template.components.filter(c => c.type !== 'review_submit' && c.type !== 'verification_progress');
510
+ const allDone = nonReview.every(c => state.completedComponents.includes(c.id));
511
+ if (allDone)
512
+ return comp;
513
+ const firstIncomplete = nonReview.find(c => !state.completedComponents.includes(c.id));
514
+ return firstIncomplete || comp;
515
+ }, [state.template.components, state.currentComponentIndex, state.completedComponents, apiKey]);
516
+ // Progression du flux (basée sur le composant effectivement affiché)
432
517
  const progress = useMemo(() => {
518
+ const idx = currentComponent
519
+ ? state.template.components.findIndex(c => c.id === currentComponent.id)
520
+ : state.currentComponentIndex;
521
+ const i = idx >= 0 ? idx : state.currentComponentIndex;
433
522
  return state.template.components.length > 0
434
- ? ((state.currentComponentIndex + 1) / state.template.components.length) * 100
523
+ ? ((i + 1) / state.template.components.length) * 100
435
524
  : 0;
436
- }, [state.currentComponentIndex, state.template.components.length, apiKey]);
525
+ }, [currentComponent, state.template.components, state.currentComponentIndex, apiKey]);
437
526
  // Vérifications de navigation
438
527
  const canGoNext = useMemo(() => {
439
528
  return state.currentComponentIndex < state.template.components.length - 1;
@@ -518,12 +607,21 @@ export const useTemplateKYCFlow = (template, onComplete, onError, onCancel, init
518
607
  }));
519
608
  }
520
609
  }, [apiKey]);
521
- // Validation d'un composant
522
- const validateComponent = useCallback((componentId) => {
610
+ // When user switches device: resume with existingSessionId + initialStep — ensure session is initialized so loadSessionData runs
611
+ useEffect(() => {
612
+ if (!existingSessionId || state.session.isInitialized)
613
+ return;
614
+ if (state.session.session_id !== existingSessionId)
615
+ return;
616
+ logger.log('Resuming on new device: initializing session so data can load', { existingSessionId, initialComponentIndex });
617
+ initializeSession();
618
+ }, [existingSessionId, state.session.session_id, state.session.isInitialized, initializeSession]);
619
+ // Validation d'un composant (dataOverride permet de valider sans attendre la mise à jour du state)
620
+ const validateComponent = useCallback((componentId, dataOverride) => {
523
621
  const component = state.template.components.find(c => c.id === componentId);
524
622
  if (!component)
525
623
  return false;
526
- const componentData = state.componentData[componentId];
624
+ const componentData = dataOverride !== undefined ? dataOverride : state.componentData[componentId];
527
625
  switch (component.type) {
528
626
  case 'id_card':
529
627
  // Vérifier si au moins un côté a été capturé
@@ -579,8 +677,8 @@ export const useTemplateKYCFlow = (template, onComplete, onError, onCancel, init
579
677
  verification: { status: 'idle', result: undefined },
580
678
  }));
581
679
  }, [ensureReviewSubmitStep, ensureVerificationProgressStep, apiKey]),
582
- // Passer au composant suivant
583
- nextComponent: useCallback(async () => {
680
+ // Passer au composant suivant (overrideData = données du step courant si déjà connues, évite un double clic)
681
+ nextComponent: useCallback(async (overrideData) => {
584
682
  if (!canGoNext)
585
683
  return;
586
684
  const currentComp = state.template.components[state.currentComponentIndex];
@@ -596,8 +694,8 @@ export const useTemplateKYCFlow = (template, onComplete, onError, onCancel, init
596
694
  isProcessing: true,
597
695
  };
598
696
  });
599
- // Valider le composant actuel
600
- if (!validateComponent(currentComp.id)) {
697
+ // Valider avec override ou state
698
+ if (!validateComponent(currentComp.id, overrideData)) {
601
699
  setState(prev => ({
602
700
  ...prev,
603
701
  isProcessing: false,
@@ -618,17 +716,14 @@ export const useTemplateKYCFlow = (template, onComplete, onError, onCancel, init
618
716
  return;
619
717
  }
620
718
  if (component.type === 'review_submit') {
621
- // Move to verification screen and mark verification in progress
622
719
  setState(prev => ({
623
720
  ...prev,
624
721
  currentComponentIndex: prev.currentComponentIndex + 1,
625
722
  completedComponents: [...prev.completedComponents, currentComp.id],
723
+ componentData: overrideData !== undefined ? { ...prev.componentData, [currentComp.id]: overrideData } : prev.componentData,
626
724
  isProcessing: false,
627
725
  verification: { status: 'in_progress' },
628
- errors: {
629
- ...prev.errors,
630
- [currentComp.id]: ''
631
- }
726
+ errors: { ...prev.errors, [currentComp.id]: '' }
632
727
  }));
633
728
  return;
634
729
  }
@@ -658,17 +753,15 @@ export const useTemplateKYCFlow = (template, onComplete, onError, onCancel, init
658
753
  ...prev,
659
754
  currentComponentIndex: prev.currentComponentIndex + 1,
660
755
  completedComponents: [...prev.completedComponents, currentComp.id],
756
+ componentData: overrideData !== undefined ? { ...prev.componentData, [currentComp.id]: overrideData } : prev.componentData,
661
757
  isProcessing: false,
662
- errors: {
663
- ...prev.errors,
664
- [currentComp.id]: ''
665
- }
758
+ errors: { ...prev.errors, [currentComp.id]: '' }
666
759
  }));
667
760
  return;
668
761
  }
669
762
  const step = serverStep === 0 && action !== 'initialize_session' ? 1 : serverStep;
670
- // Build payload data per action
671
- const payloadData = buildPayloadForComponent(action, component, state.componentData[currentComp.id], templateId, step);
763
+ const currentStepData = overrideData !== undefined ? overrideData : state.componentData[currentComp.id];
764
+ const payloadData = buildPayloadForComponent(action, component, currentStepData, templateId, step);
672
765
  console.log('payloadData', action, apiKey);
673
766
  await kycService.verificationSession({
674
767
  session_id: state.session.session_id,
@@ -680,17 +773,14 @@ export const useTemplateKYCFlow = (template, onComplete, onError, onCancel, init
680
773
  apiKey: apiKey ?? "-",
681
774
  });
682
775
  logger.log("currentComp state", truncateFields(state));
683
- // Marquer comme complété et passer au suivant
684
776
  setState(prev => ({
685
777
  ...prev,
686
778
  currentComponentIndex: prev.currentComponentIndex + 1,
687
779
  completedComponents: [...prev.completedComponents, currentComp.id],
780
+ componentData: overrideData !== undefined ? { ...prev.componentData, [currentComp.id]: overrideData } : prev.componentData,
688
781
  isProcessing: false,
689
782
  ...(action === "location_permission" ? { permissionGranted: true } : {}),
690
- errors: {
691
- ...prev.errors,
692
- [currentComp.id]: ''
693
- }
783
+ errors: { ...prev.errors, [currentComp.id]: '' }
694
784
  }));
695
785
  }
696
786
  catch (error) {
@@ -740,8 +830,8 @@ export const useTemplateKYCFlow = (template, onComplete, onError, onCancel, init
740
830
  }));
741
831
  }, [apiKey]),
742
832
  // Valider un composant
743
- validateComponent: useCallback((componentId) => {
744
- return validateComponent(componentId);
833
+ validateComponent: useCallback((componentId, dataOverride) => {
834
+ return validateComponent(componentId, dataOverride);
745
835
  }, [validateComponent, apiKey]),
746
836
  // complet verification
747
837
  submitVerification: useCallback(async () => {
@@ -814,6 +904,7 @@ export const useTemplateKYCFlow = (template, onComplete, onError, onCancel, init
814
904
  getLocalizedText,
815
905
  initializeSession,
816
906
  env,
907
+ apiKey,
817
908
  };
818
909
  };
819
910
  //# sourceMappingURL=useTemplateKYCFlow.js.map