@sanctum-key/react-native-sdk 1.0.47 → 1.0.49
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/IDCardCapture.d.ts.map +1 -1
- package/build/src/components/KYCElements/IDCardCapture.js +84 -23
- package/build/src/components/KYCElements/IDCardCapture.js.map +1 -1
- package/build/src/components/KYCElements/VerificationProgressTemplate.d.ts.map +1 -1
- package/build/src/components/KYCElements/VerificationProgressTemplate.js +91 -49
- package/build/src/components/KYCElements/VerificationProgressTemplate.js.map +1 -1
- package/build/src/hooks/useTemplateKYCFlow.d.ts.map +1 -1
- package/build/src/hooks/useTemplateKYCFlow.js +15 -1
- package/build/src/hooks/useTemplateKYCFlow.js.map +1 -1
- package/build/src/modules/api/CardAuthentification.d.ts.map +1 -1
- package/build/src/modules/api/CardAuthentification.js +37 -59
- package/build/src/modules/api/CardAuthentification.js.map +1 -1
- package/build/src/modules/api/KYCService.d.ts.map +1 -1
- package/build/src/modules/api/KYCService.js +7 -2
- package/build/src/modules/api/KYCService.js.map +1 -1
- package/build/src/types/KYC.types.d.ts +1 -0
- package/build/src/types/KYC.types.d.ts.map +1 -1
- package/build/src/types/KYC.types.js.map +1 -1
- package/package.json +1 -1
- package/src/components/KYCElements/IDCardCapture.tsx +98 -24
- package/src/components/KYCElements/VerificationProgressTemplate.tsx +95 -52
- package/src/hooks/useTemplateKYCFlow.tsx +19 -1
- package/src/modules/api/CardAuthentification.ts +205 -238
- package/src/modules/api/KYCService.ts +9 -2
- package/src/types/KYC.types.ts +1 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"KYC.types.js","sourceRoot":"","sources":["../../../src/types/KYC.types.ts"],"names":[],"mappings":"AA+UA,MAAM,CAAC,MAAM,6BAA6B,GAA2C;IACnF,aAAa,EAAE,IAAI;IACnB,QAAQ,EAAE,IAAI;IACd,eAAe,EAAE,IAAI;IACrB,qBAAqB,EAAE,KAAK;IAC5B,mBAAmB,EAAE,IAAI;IACzB,WAAW,EAAE,IAAI;IACjB,WAAW,EAAE,IAAI;IACjB,EAAE,EAAE,IAAI;IACR,EAAE,EAAE,IAAI;IACR,EAAE,EAAE,IAAI;IACR,GAAG,EAAE,KAAK;IACV,EAAE,EAAE,IAAI;IACR,EAAE,EAAE,IAAI;CACT,CAAA;AAED,MAAM,CAAC,MAAM,6BAA6B,GAA2C;IACnF,WAAW,EAAE,eAAe;IAC5B,QAAQ,EAAE,UAAU;IACpB,cAAc,EAAE,iBAAiB;IACjC,mBAAmB,EAAE,uBAAuB;IAC5C,kBAAkB,EAAE,qBAAqB;IACzC,UAAU,EAAE,aAAa;IACzB,eAAe,EAAE,qBAAqB;CACvC,CAAA","sourcesContent":["import { VerificationResult } from \"../modules/api/types\";\n\n// Types pour les documents\nexport interface Document {\n id: string;\n type: DocumentType;\n imageUri: string;\n uploadedAt: Date;\n validated: boolean;\n extractedData?: DocumentData;\n\n}\n\nexport type DocumentType = 'passport' | 'id_card' | 'drivers_license';\n\nexport interface DocumentData {\n firstName?: string;\n lastName?: string;\n dateOfBirth?: string;\n documentNumber?: string;\n expiryDate?: string;\n nationality?: string;\n}\n\n// Types pour les selfies\nexport interface Selfie {\n id: string;\n imageUri: string;\n capturedAt: Date;\n validated: boolean;\n faceDetected: boolean;\n}\n\n// Types pour la session KYC\nexport interface KYCSession {\n id: string;\n userId: string;\n status: KYCStatus;\n currentStep: number;\n documents: Document[];\n selfie?: Selfie;\n verificationId?: string;\n createdAt: Date;\n updatedAt: Date;\n completedAt?: Date;\n}\n\nexport type KYCStatus = 'pending' | 'in_progress' | 'completed' | 'failed' | 'rejected';\n\n// Types pour les étapes du processus\nexport interface KYCStep {\n id: number;\n title: string;\n description: string;\n isCompleted: boolean;\n isRequired: boolean;\n}\n\n// Types pour les erreurs\nexport interface KYCError {\n code: string;\n message: string;\n field?: string;\n details?: any;\n}\n\n// Types pour les réponses API\nexport interface KYCResponse {\n success: boolean;\n data?: any;\n error?: KYCError;\n message?: string;\n}\n\n// Types pour les événements\nexport interface KYCEvent {\n type: 'step_completed' | 'document_uploaded' | 'selfie_captured' | 'verification_started' | 'verification_completed';\n timestamp: Date;\n data?: any;\n}\n\n// Types pour les configurations\nexport interface KYCConfig {\n apiBaseUrl: string;\n apiKey: string;\n timeout: number;\n retryAttempts: number;\n enableDebug: boolean;\n}\n\n// Types pour les hooks\nexport interface UseKYCReturn {\n session: KYCSession | null;\n currentStep: number;\n isProcessing: boolean;\n error: KYCError | null;\n startSession: (userId: string) => void;\n nextStep: () => void;\n previousStep: () => void;\n uploadDocument: (document: Document) => Promise<void>;\n captureSelfie: (selfie: Selfie) => Promise<void>;\n submitVerification: () => Promise<void>;\n resetSession: () => void;\n}\n\n// ===== NOUVEAUX TYPES POUR CONFIGURATION AUTOMATIQUE =====\n\n// Types d'éléments KYC configurables\nexport type KYCElementType =\n | 'country_selection'\n | 'document_type_selection'\n | 'camera_capture'\n | 'file_upload'\n | 'selfie_capture'\n | 'verification'\n | 'completion';\n//KYCElementType shorted\n\n\n// Configuration d'un élément KYC\nexport interface KYCElement {\n id: string;\n type: KYCElementType;\n title: string;\n description: string;\n isRequired: boolean;\n isVisible: boolean;\n order: number;\n validation?: KYCValidation;\n options?: KYCElementOption[];\n cameraConfig?: KYCCameraConfig;\n uploadConfig?: KYCUploadConfig;\n}\n\n// Options pour les éléments de sélection\nexport interface KYCElementOption {\n value: string;\n label: string;\n icon?: string;\n description?: string;\n}\n\n// Configuration de validation\nexport interface KYCValidation {\n required: boolean;\n minLength?: number;\n maxLength?: number;\n pattern?: string;\n customValidator?: (value: any) => boolean | string;\n}\n\n// Configuration de la caméra\nexport interface KYCCameraConfig {\n aspectRatio?: number;\n quality?: number;\n flashMode?: 'on' | 'off' | 'auto';\n cameraType?: 'front' | 'back';\n allowRetake: boolean;\n maxRetakes?: number;\n overlay?: {\n showGuide: boolean;\n guideText?: string;\n guideImage?: string;\n };\n}\n\n// Configuration d'upload\nexport interface KYCUploadConfig {\n allowedTypes: string[];\n maxFiles: number;\n maxFileSize?: number;\n allowMultiple: boolean;\n}\n\n// Configuration complète du flux KYC\nexport interface KYCFlowConfig {\n id: string;\n name: string;\n description: string;\n version: string;\n elements: KYCElement[];\n settings: {\n allowBackNavigation: boolean;\n showProgressBar: boolean;\n showStepNumbers: boolean;\n autoAdvance: boolean;\n enableRetry: boolean;\n maxRetries: number;\n };\n styling: {\n primaryColor: string;\n secondaryColor: string;\n backgroundColor: string;\n textColor: string;\n borderRadius: number;\n fontFamily?: string;\n };\n localization: {\n language: string;\n translations: Record<string, Record<string, string>>;\n };\n}\n\n// État du flux KYC\nexport interface KYCFlowState {\n config: KYCFlowConfig;\n currentElementIndex: number;\n completedElements: string[];\n elementData: Record<string, any>;\n errors: Record<string, string>;\n isProcessing: boolean;\n}\n\n// Actions pour le flux KYC\nexport interface KYCFlowActions {\n initializeFlow: (config: KYCFlowConfig) => void;\n nextElement: () => void;\n previousElement: () => void;\n goToElement: (elementId: string) => void;\n updateElementData: (elementId: string, data: any) => void;\n validateElement: (elementId: string) => boolean;\n submitFlow: () => Promise<void>;\n resetFlow: () => void;\n}\n\n// Hook pour le flux KYC\nexport interface UseKYCFlowReturn {\n state: KYCFlowState;\n actions: KYCFlowActions;\n currentElement: KYCElement | null;\n progress: number;\n canGoNext: boolean;\n canGoPrevious: boolean;\n isComplete: boolean;\n}\n\n// ===== TYPES FOR ORIENTATION VIDEO PROCESSING =====\n\n// Orientation types for liveness detection\nexport type OrientationType = 'center' | 'left' | 'right';\n\n// Individual orientation capture result\nexport interface OrientationCapture {\n captured: boolean;\n frame: number | null;\n}\n\n// Complete orientation video processing result\nexport interface OrientationVideoResult {\n center: OrientationCapture;\n right: OrientationCapture;\n left: OrientationCapture;\n}\n\n// Orientation video processing request\nexport interface OrientationVideoRequest {\n file: string; // Base64 encoded video or file path\n}\n\n// Orientation video processing response\nexport interface OrientationVideoResponse {\n success: boolean;\n data?: OrientationVideoResult;\n error?: KYCError;\n message?: string;\n}\n\n// Configuration for orientation video capture\nexport interface OrientationVideoConfig {\n duration: number; // Duration in seconds\n orientations: OrientationType[];\n instructions: Record<OrientationType, string>;\n showPreview: boolean;\n allowRetake: boolean;\n maxRetakes: number;\n quality: number;\n aspectRatio: number;\n}\n\n// State for orientation video capture\nexport interface OrientationVideoState {\n isRecording: boolean;\n currentOrientation: OrientationType | null;\n recordedVideo: string | null;\n processingResult: OrientationVideoResult | null;\n isProcessing: boolean;\n error: string | null;\n retakeCount: number;\n}\n\n// Actions for orientation video capture\nexport interface OrientationVideoActions {\n startRecording: () => void;\n stopRecording: () => void;\n processVideo: () => Promise<OrientationVideoResult>;\n retake: () => void;\n reset: () => void;\n setCurrentOrientation: (orientation: OrientationType) => void;\n}\n\n// ===== NOUVEAUX TYPES POUR TEMPLATE JSON =====\n\n// Types pour les labels et instructions multilingues\nexport interface LocalizedText {\n en: string;\n fr: string;\n [key: string]: string; // Support pour d'autres langues\n}\n\n// Configuration UI pour les composants\nexport interface ComponentUI {\n icon?: string;\n themeColor?: string;\n buttonText: LocalizedText | Record<string, LocalizedText>;\n backgroundColor?: string;\n textColor?: string;\n borderRadius?: number;\n}\n\n// Types de documents gouvernementaux supportés\nexport type GovernmentDocumentType =\n | 'identity_card'\n | 'passport'\n | 'drivers_licence'\n | 'health_insurance_card'\n | 'permanent_residence'\n | 'national_id'\n | 'work_permit'\n | 'id'\n | 'pp'\n | 'dl'\n | 'hic'\n | 'pr'\n | 'wp'\n\nexport const GovernmentDocumentTypeShorted: Record<GovernmentDocumentType, string> = {\n identity_card: 'id',\n passport: 'pp',\n drivers_licence: 'dl',\n health_insurance_card: 'hic',\n permanent_residence: 'pr',\n national_id: 'id',\n work_permit: 'wp',\n id: 'id',\n pp: 'pp',\n dl: 'dl',\n hic: 'hic',\n pr: 'pr',\n wp: 'wp',\n}\n\nexport const GovernmentDocumentTypeBackend: Record<string, GovernmentDocumentType> = {\n national_id: 'identity_card',\n passport: 'passport',\n driversLicense: 'drivers_licence',\n healthInsuranceCard: 'health_insurance_card',\n permanentResidence: 'permanent_residence',\n workPermit: 'work_permit',\n residencePermit: 'permanent_residence',\n}\n\n// Configuration spécifique pour chaque type de composant\nexport interface IDCardConfig {\n sides: string[];\n allowed_formats: string[];\n max_size_mb: number;\n document_types: GovernmentDocumentType[]; // Types de documents disponibles\n bbox_configs?: Record<GovernmentDocumentType, {\n xMin: number;\n yMin: number;\n xMax: number;\n yMax: number;\n borderColor?: string;\n borderWidth?: number;\n cornerRadius?: number;\n }>;\n}\n\n\nexport interface SelfieConfig {\n liveness_check: boolean;\n max_attempts: number;\n orientations: ('center' | 'left' | 'right')[]; // Tableau d'orientations à capturer\n}\n\nexport interface FileUploadConfig {\n allowed_formats: string[];\n max_size_mb: number;\n required: boolean;\n}\n\nexport interface LocationConfig {\n accuracy: string;\n required: boolean;\n}\n\nexport interface CountrySelectionConfig {\n allowed_countries: string[];\n default_country: string;\n required: boolean;\n}\n\n// Union type pour toutes les configurations\nexport type ComponentConfig =\n | IDCardConfig\n | SelfieConfig\n | FileUploadConfig\n | LocationConfig\n | CountrySelectionConfig\n | CountrySelectionConfig\n | WelcomeConfig\n | EmailVerificationConfig\n | PhoneVerificationConfig\n | PersonalInformationConfig\n | AdditionalDocumentsConfig;\n\n// Nouveau type de composant basé sur le template JSON\nexport interface TemplateComponent {\n id: number;\n type: 'id_card' | 'selfie' | 'file_upload' | 'location' | 'country_selection' | 'initialization' | 'review_submit' | 'verification_progress' | 'welcome' | 'email_verification' | 'phone_verification' | 'personal_information' | 'additional_documents';\n order: number;\n templateId?: number;\n labels: LocalizedText | Record<string, LocalizedText>;\n instructions: LocalizedText | Record<string, LocalizedText>;\n ui: ComponentUI | Record<string, ComponentUI>;\n config: ComponentConfig;\n}\n\n// Template KYC complet\nexport interface KYCTemplate {\n id: number;\n name: LocalizedText;\n description: LocalizedText;\n version: string;\n components: TemplateComponent[];\n}\n\n// État pour le template KYC\nexport interface TemplateState {\n template: KYCTemplate;\n currentComponentIndex: number;\n completedComponents: number[];\n componentData: Record<number, any>;\n errors: Record<number, string>;\n isProcessing: boolean;\n currentLanguage: string;\n showCustomStepper: boolean;\n session: SessionState;\n verification: VerificationState;\n}\n\nexport interface SessionState {\n session_id: string;\n token: string;\n isInitialized: boolean;\n isProcessing: boolean;\n error: string | null;\n /** True once loadSessionData has run (for resume flow); allows UI to show loading until country/data restored */\n sessionDataRestored?: boolean;\n}\n\nexport type VerificationStatus = 'idle' | 'in_progress' | 'success' | 'failed';\n\n\n\nexport interface VerificationState {\n status: VerificationStatus;\n result?: VerificationResult;\n}\n\n// Actions pour le template KYC\nexport interface TemplateActions {\n initializeTemplate: (template: KYCTemplate) => void;\n nextComponent: (overrideData?: any) => void;\n previousComponent: () => void;\n goToComponent: (componentId: number) => void;\n updateComponentData: (componentId: number, data: any) => void;\n validateComponent: (componentId: number, dataOverride?: any) => boolean;\n submitTemplate: () => Promise<void>;\n resetTemplate: () => void;\n setLanguage: (language: string) => void;\n showCustomStepper: (show: boolean) => void;\n setVerificationState: (state: VerificationState) => void;\n submitVerification: () => Promise<void>;\n}\n\n// Hook pour le template KYC (return type of useTemplateKYCFlow)\nexport interface UseTemplateReturn {\n state: TemplateState;\n actions: TemplateActions;\n currentComponent: TemplateComponent | null;\n progress: number;\n canGoNext: boolean;\n canGoPrevious: boolean;\n isComplete: boolean;\n getLocalizedText: (text: LocalizedText) => string;\n initializeSession: () => Promise<void>;\n env: 'PRODUCTION' | 'SANDBOX';\n /** Optional API key for backend auth (e.g. email verification endpoints). */\n apiKey?: string;\n}\n\n\nexport interface ISilentCaptureResult {\n success: boolean;\n path?: string;\n mrz?: string;\n barcode?: string | any; \n error?: string;\n isAnalyzing?: boolean;\n bbox?: IBbox;\n country?: string;\n documentType?: string;\n templatePath?: string;\n lpipsScore?: number;\n}\n\nexport interface IBbox {\n minX: number;\n minY: number;\n width: number;\n height: number;\n}\n\nexport interface ICountryMapping {\n py_file?: string;\n front?: string;\n back?: string;\n keys_found?: string | null;\n mrz_type?: string;\n}\n\nexport interface Country {\n code?: string; // ISO 3166-1 alpha-2\n name: string;\n name_en: string;\n flag: string;\n regionMapping?: ICountryMapping;\n hasRegions?: boolean;\n}\n\n// ===== TYPES FOR BACKEND TEMPLATE FORMAT =====\n\n// Backend component configuration types\nexport interface WelcomeConfig {\n subtitle?: string;\n buttonText?: string;\n requirements?: string[];\n estimatedTime?: string;\n consentOptions?: {\n showPrivacyPolicy?: boolean;\n showTermsOfService?: boolean;\n showMarketingConsent?: boolean;\n };\n welcomeMessage?: string;\n showEstimatedTime?: boolean;\n}\n\nexport interface LocationCaptureConfig {\n showMap?: boolean;\n accuracy?: string;\n required?: boolean;\n maxDistance?: number;\n captureMethod?: string;\n allowManualEntry?: boolean;\n countryRestrictions?: string[];\n}\n\nexport interface GovernmentIdConfig {\n required?: boolean;\n allowSkip?: boolean;\n cameraOverlay?: {\n bbox?: {\n xMax?: number;\n xMin?: number;\n yMax?: number;\n yMin?: number;\n borderColor?: string;\n borderWidth?: number;\n cornerRadius?: number;\n };\n };\n documentTypes?: string[];\n requiredSides?: string;\n cameraSettings?: {\n quality?: number;\n flashMode?: string;\n maxRetakes?: number;\n allowRetake?: boolean;\n aspectRatio?: number;\n };\n validationRules?: string[];\n regionsByCountry?: Record<string, any>;\n selectedCountries?: string[];\n authenticationMethods?: Record<string, Record<string, {\n back?: string[];\n front?: string[];\n mrzTypes?: string[];\n }>>;\n documentTypesByCountry?: Record<string, string[]>;\n instructionsByDocumentType?: Record<string, LocalizedText>;\n}\n\nexport interface SelfieCaptureConfig {\n timeout?: number;\n maxAttempts?: number;\n faceMatching?: boolean;\n captureMethod?: string;\n qualityChecks?: string[];\n livenessChecks?: string[];\n matchThreshold?: string;\n livenessEnabled?: boolean;\n}\n\nexport interface ReviewSubmitConfig {\n allowEditing?: boolean;\n displayOptions?: string[];\n requireConsent?: boolean;\n postSubmitActions?: string[];\n}\n\nexport type BackendComponentConfig =\n | WelcomeConfig\n | LocationCaptureConfig\n | GovernmentIdConfig\n | SelfieCaptureConfig\n | ReviewSubmitConfig\n | Record<string, any>;\n\n// Backend component translation structure\nexport interface BackendComponentTranslations {\n en?: {\n title?: string;\n description?: string;\n instructions?: string;\n buttonText?: string;\n consentText?: string;\n successMessage?: string;\n };\n fr?: {\n title?: string;\n description?: string;\n instructions?: string;\n buttonText?: string;\n consentText?: string;\n successMessage?: string;\n };\n [key: string]: {\n title?: string;\n description?: string;\n instructions?: string;\n buttonText?: string;\n consentText?: string;\n successMessage?: string;\n } | undefined;\n}\n\n// Backend template component\nexport interface BackendTemplateComponent {\n id: string;\n type: 'welcome' | 'location-capture' | 'government-id' | 'selfie-capture' | 'review-submit' | 'verification-progress' | 'email-verification' | 'phone-verification' | 'personal-information' | 'additional-documents';\n order: number;\n config: BackendComponentConfig;\n required?: boolean;\n translations: BackendComponentTranslations;\n}\n\n// Backend template structure\nexport interface BackendKYCTemplate {\n id: string;\n name: {\n en: string;\n fr: string;\n [key: string]: string;\n };\n status?: string;\n version?: string;\n components: BackendTemplateComponent[];\n description?: {\n en: string;\n fr: string;\n [key: string]: string;\n };\n}\n\n// ===== NEW CONFIG TYPES FOR INPUT VERIFICATION =====\n\nexport interface EmailVerificationConfig {\n allowResend: boolean;\n otpSettings: {\n codeLength: number;\n expiryTime: number;\n };\n resendCooldown: number;\n emailValidation: {\n corporateOnly: boolean;\n validateFormat: boolean;\n checkDisposable: boolean;\n checkDeliverability: boolean;\n };\n verificationMethod: string;\n required?: boolean;\n}\n\nexport interface PhoneVerificationConfig {\n required: boolean;\n codeExpiry: number;\n codeLength: number;\n maxAttempts: number;\n resendDelay: number;\n countryRestrictions: string;\n verificationMethods: string[];\n allowSkipForVerified: boolean;\n}\n\nexport interface PersonalInfoField {\n id: string;\n type: string;\n label: string;\n required: boolean;\n placeholder?: string;\n autoFillFromId?: boolean;\n minAge?: number;\n verifyAge?: boolean;\n dateFormat?: string;\n}\n\nexport interface PersonalInformationConfig {\n fields: PersonalInfoField[];\n validationSettings: {\n validateOnBlur: boolean;\n showAllErrorsAtOnce: boolean;\n preventSubmissionWithErrors: boolean;\n };\n required?: boolean;\n}\n\nexport interface DocumentCategory {\n enabled: boolean;\n category: string;\n documentTypes: string[];\n}\n\nexport interface AdditionalDocumentsConfig {\n required: boolean;\n maxSizeEach: string;\n maxDocuments: number;\n minDocuments: number;\n documentCategories: DocumentCategory[];\n customDocumentTypes: any[];\n showBasedOnUserProfile: boolean;\n}\n\n// API response for template\nexport interface TemplateAPIResponse {\n id: string;\n name: {\n en: string;\n fr: string;\n [key: string]: string;\n };\n status?: string;\n version?: string;\n components: BackendTemplateComponent[];\n description?: {\n en: string;\n fr: string;\n [key: string]: string;\n };\n}\n\n// ===== NEW CONFIG TYPES FOR INPUT VERIFICATION =====\n\nexport interface EmailVerificationConfig {\n allowResend: boolean;\n otpSettings: {\n codeLength: number;\n expiryTime: number;\n };\n resendCooldown: number;\n emailValidation: {\n corporateOnly: boolean;\n validateFormat: boolean;\n checkDisposable: boolean;\n checkDeliverability: boolean;\n };\n verificationMethod: string;\n required?: boolean;\n}\n\nexport interface PhoneVerificationConfig {\n required: boolean;\n codeExpiry: number;\n codeLength: number;\n maxAttempts: number;\n resendDelay: number;\n countryRestrictions: string;\n verificationMethods: string[];\n allowSkipForVerified: boolean;\n}\n\nexport interface PersonalInfoField {\n id: string;\n type: string;\n label: string;\n required: boolean;\n placeholder?: string;\n autoFillFromId?: boolean;\n minAge?: number;\n verifyAge?: boolean;\n dateFormat?: string;\n}\n\nexport interface PersonalInformationConfig {\n fields: PersonalInfoField[];\n validationSettings: {\n validateOnBlur: boolean;\n showAllErrorsAtOnce: boolean;\n preventSubmissionWithErrors: boolean;\n };\n required?: boolean;\n}\n\nexport interface DocumentCategory {\n enabled: boolean;\n category: string;\n documentTypes: string[];\n}\n\nexport interface AdditionalDocumentsConfig {\n required: boolean;\n maxSizeEach: string;\n maxDocuments: number;\n minDocuments: number;\n documentCategories: DocumentCategory[];\n customDocumentTypes: any[];\n showBasedOnUserProfile: boolean;\n}"]}
|
|
1
|
+
{"version":3,"file":"KYC.types.js","sourceRoot":"","sources":["../../../src/types/KYC.types.ts"],"names":[],"mappings":"AA+UA,MAAM,CAAC,MAAM,6BAA6B,GAA2C;IACnF,aAAa,EAAE,IAAI;IACnB,QAAQ,EAAE,IAAI;IACd,eAAe,EAAE,IAAI;IACrB,qBAAqB,EAAE,KAAK;IAC5B,mBAAmB,EAAE,IAAI;IACzB,WAAW,EAAE,IAAI;IACjB,WAAW,EAAE,IAAI;IACjB,EAAE,EAAE,IAAI;IACR,EAAE,EAAE,IAAI;IACR,EAAE,EAAE,IAAI;IACR,GAAG,EAAE,KAAK;IACV,EAAE,EAAE,IAAI;IACR,EAAE,EAAE,IAAI;CACT,CAAA;AAED,MAAM,CAAC,MAAM,6BAA6B,GAA2C;IACnF,WAAW,EAAE,eAAe;IAC5B,QAAQ,EAAE,UAAU;IACpB,cAAc,EAAE,iBAAiB;IACjC,mBAAmB,EAAE,uBAAuB;IAC5C,kBAAkB,EAAE,qBAAqB;IACzC,UAAU,EAAE,aAAa;IACzB,eAAe,EAAE,qBAAqB;CACvC,CAAA","sourcesContent":["import { VerificationResult } from \"../modules/api/types\";\n\n// Types pour les documents\nexport interface Document {\n id: string;\n type: DocumentType;\n imageUri: string;\n uploadedAt: Date;\n validated: boolean;\n extractedData?: DocumentData;\n\n}\n\nexport type DocumentType = 'passport' | 'id_card' | 'drivers_license';\n\nexport interface DocumentData {\n firstName?: string;\n lastName?: string;\n dateOfBirth?: string;\n documentNumber?: string;\n expiryDate?: string;\n nationality?: string;\n}\n\n// Types pour les selfies\nexport interface Selfie {\n id: string;\n imageUri: string;\n capturedAt: Date;\n validated: boolean;\n faceDetected: boolean;\n}\n\n// Types pour la session KYC\nexport interface KYCSession {\n id: string;\n userId: string;\n status: KYCStatus;\n currentStep: number;\n documents: Document[];\n selfie?: Selfie;\n verificationId?: string;\n createdAt: Date;\n updatedAt: Date;\n completedAt?: Date;\n}\n\nexport type KYCStatus = 'pending' | 'in_progress' | 'completed' | 'failed' | 'rejected';\n\n// Types pour les étapes du processus\nexport interface KYCStep {\n id: number;\n title: string;\n description: string;\n isCompleted: boolean;\n isRequired: boolean;\n}\n\n// Types pour les erreurs\nexport interface KYCError {\n code: string;\n message: string;\n field?: string;\n details?: any;\n}\n\n// Types pour les réponses API\nexport interface KYCResponse {\n success: boolean;\n data?: any;\n error?: KYCError;\n message?: string;\n}\n\n// Types pour les événements\nexport interface KYCEvent {\n type: 'step_completed' | 'document_uploaded' | 'selfie_captured' | 'verification_started' | 'verification_completed';\n timestamp: Date;\n data?: any;\n}\n\n// Types pour les configurations\nexport interface KYCConfig {\n apiBaseUrl: string;\n apiKey: string;\n timeout: number;\n retryAttempts: number;\n enableDebug: boolean;\n}\n\n// Types pour les hooks\nexport interface UseKYCReturn {\n session: KYCSession | null;\n currentStep: number;\n isProcessing: boolean;\n error: KYCError | null;\n startSession: (userId: string) => void;\n nextStep: () => void;\n previousStep: () => void;\n uploadDocument: (document: Document) => Promise<void>;\n captureSelfie: (selfie: Selfie) => Promise<void>;\n submitVerification: () => Promise<void>;\n resetSession: () => void;\n}\n\n// ===== NOUVEAUX TYPES POUR CONFIGURATION AUTOMATIQUE =====\n\n// Types d'éléments KYC configurables\nexport type KYCElementType =\n | 'country_selection'\n | 'document_type_selection'\n | 'camera_capture'\n | 'file_upload'\n | 'selfie_capture'\n | 'verification'\n | 'completion';\n//KYCElementType shorted\n\n\n// Configuration d'un élément KYC\nexport interface KYCElement {\n id: string;\n type: KYCElementType;\n title: string;\n description: string;\n isRequired: boolean;\n isVisible: boolean;\n order: number;\n validation?: KYCValidation;\n options?: KYCElementOption[];\n cameraConfig?: KYCCameraConfig;\n uploadConfig?: KYCUploadConfig;\n}\n\n// Options pour les éléments de sélection\nexport interface KYCElementOption {\n value: string;\n label: string;\n icon?: string;\n description?: string;\n}\n\n// Configuration de validation\nexport interface KYCValidation {\n required: boolean;\n minLength?: number;\n maxLength?: number;\n pattern?: string;\n customValidator?: (value: any) => boolean | string;\n}\n\n// Configuration de la caméra\nexport interface KYCCameraConfig {\n aspectRatio?: number;\n quality?: number;\n flashMode?: 'on' | 'off' | 'auto';\n cameraType?: 'front' | 'back';\n allowRetake: boolean;\n maxRetakes?: number;\n overlay?: {\n showGuide: boolean;\n guideText?: string;\n guideImage?: string;\n };\n}\n\n// Configuration d'upload\nexport interface KYCUploadConfig {\n allowedTypes: string[];\n maxFiles: number;\n maxFileSize?: number;\n allowMultiple: boolean;\n}\n\n// Configuration complète du flux KYC\nexport interface KYCFlowConfig {\n id: string;\n name: string;\n description: string;\n version: string;\n elements: KYCElement[];\n settings: {\n allowBackNavigation: boolean;\n showProgressBar: boolean;\n showStepNumbers: boolean;\n autoAdvance: boolean;\n enableRetry: boolean;\n maxRetries: number;\n };\n styling: {\n primaryColor: string;\n secondaryColor: string;\n backgroundColor: string;\n textColor: string;\n borderRadius: number;\n fontFamily?: string;\n };\n localization: {\n language: string;\n translations: Record<string, Record<string, string>>;\n };\n}\n\n// État du flux KYC\nexport interface KYCFlowState {\n config: KYCFlowConfig;\n currentElementIndex: number;\n completedElements: string[];\n elementData: Record<string, any>;\n errors: Record<string, string>;\n isProcessing: boolean;\n}\n\n// Actions pour le flux KYC\nexport interface KYCFlowActions {\n initializeFlow: (config: KYCFlowConfig) => void;\n nextElement: () => void;\n previousElement: () => void;\n goToElement: (elementId: string) => void;\n updateElementData: (elementId: string, data: any) => void;\n validateElement: (elementId: string) => boolean;\n submitFlow: () => Promise<void>;\n resetFlow: () => void;\n}\n\n// Hook pour le flux KYC\nexport interface UseKYCFlowReturn {\n state: KYCFlowState;\n actions: KYCFlowActions;\n currentElement: KYCElement | null;\n progress: number;\n canGoNext: boolean;\n canGoPrevious: boolean;\n isComplete: boolean;\n}\n\n// ===== TYPES FOR ORIENTATION VIDEO PROCESSING =====\n\n// Orientation types for liveness detection\nexport type OrientationType = 'center' | 'left' | 'right';\n\n// Individual orientation capture result\nexport interface OrientationCapture {\n captured: boolean;\n frame: number | null;\n}\n\n// Complete orientation video processing result\nexport interface OrientationVideoResult {\n center: OrientationCapture;\n right: OrientationCapture;\n left: OrientationCapture;\n}\n\n// Orientation video processing request\nexport interface OrientationVideoRequest {\n file: string; // Base64 encoded video or file path\n}\n\n// Orientation video processing response\nexport interface OrientationVideoResponse {\n success: boolean;\n data?: OrientationVideoResult;\n error?: KYCError;\n message?: string;\n}\n\n// Configuration for orientation video capture\nexport interface OrientationVideoConfig {\n duration: number; // Duration in seconds\n orientations: OrientationType[];\n instructions: Record<OrientationType, string>;\n showPreview: boolean;\n allowRetake: boolean;\n maxRetakes: number;\n quality: number;\n aspectRatio: number;\n}\n\n// State for orientation video capture\nexport interface OrientationVideoState {\n isRecording: boolean;\n currentOrientation: OrientationType | null;\n recordedVideo: string | null;\n processingResult: OrientationVideoResult | null;\n isProcessing: boolean;\n error: string | null;\n retakeCount: number;\n}\n\n// Actions for orientation video capture\nexport interface OrientationVideoActions {\n startRecording: () => void;\n stopRecording: () => void;\n processVideo: () => Promise<OrientationVideoResult>;\n retake: () => void;\n reset: () => void;\n setCurrentOrientation: (orientation: OrientationType) => void;\n}\n\n// ===== NOUVEAUX TYPES POUR TEMPLATE JSON =====\n\n// Types pour les labels et instructions multilingues\nexport interface LocalizedText {\n en: string;\n fr: string;\n [key: string]: string; // Support pour d'autres langues\n}\n\n// Configuration UI pour les composants\nexport interface ComponentUI {\n icon?: string;\n themeColor?: string;\n buttonText: LocalizedText | Record<string, LocalizedText>;\n backgroundColor?: string;\n textColor?: string;\n borderRadius?: number;\n}\n\n// Types de documents gouvernementaux supportés\nexport type GovernmentDocumentType =\n | 'identity_card'\n | 'passport'\n | 'drivers_licence'\n | 'health_insurance_card'\n | 'permanent_residence'\n | 'national_id'\n | 'work_permit'\n | 'id'\n | 'pp'\n | 'dl'\n | 'hic'\n | 'pr'\n | 'wp'\n\nexport const GovernmentDocumentTypeShorted: Record<GovernmentDocumentType, string> = {\n identity_card: 'id',\n passport: 'pp',\n drivers_licence: 'dl',\n health_insurance_card: 'hic',\n permanent_residence: 'pr',\n national_id: 'id',\n work_permit: 'wp',\n id: 'id',\n pp: 'pp',\n dl: 'dl',\n hic: 'hic',\n pr: 'pr',\n wp: 'wp',\n}\n\nexport const GovernmentDocumentTypeBackend: Record<string, GovernmentDocumentType> = {\n national_id: 'identity_card',\n passport: 'passport',\n driversLicense: 'drivers_licence',\n healthInsuranceCard: 'health_insurance_card',\n permanentResidence: 'permanent_residence',\n workPermit: 'work_permit',\n residencePermit: 'permanent_residence',\n}\n\n// Configuration spécifique pour chaque type de composant\nexport interface IDCardConfig {\n sides: string[];\n allowed_formats: string[];\n max_size_mb: number;\n document_types: GovernmentDocumentType[]; // Types de documents disponibles\n bbox_configs?: Record<GovernmentDocumentType, {\n xMin: number;\n yMin: number;\n xMax: number;\n yMax: number;\n borderColor?: string;\n borderWidth?: number;\n cornerRadius?: number;\n }>;\n}\n\n\nexport interface SelfieConfig {\n liveness_check: boolean;\n max_attempts: number;\n orientations: ('center' | 'left' | 'right')[]; // Tableau d'orientations à capturer\n}\n\nexport interface FileUploadConfig {\n allowed_formats: string[];\n max_size_mb: number;\n required: boolean;\n}\n\nexport interface LocationConfig {\n accuracy: string;\n required: boolean;\n}\n\nexport interface CountrySelectionConfig {\n allowed_countries: string[];\n default_country: string;\n required: boolean;\n}\n\n// Union type pour toutes les configurations\nexport type ComponentConfig =\n | IDCardConfig\n | SelfieConfig\n | FileUploadConfig\n | LocationConfig\n | CountrySelectionConfig\n | CountrySelectionConfig\n | WelcomeConfig\n | EmailVerificationConfig\n | PhoneVerificationConfig\n | PersonalInformationConfig\n | AdditionalDocumentsConfig;\n\n// Nouveau type de composant basé sur le template JSON\nexport interface TemplateComponent {\n id: number;\n type: 'id_card' | 'selfie' | 'file_upload' | 'location' | 'country_selection' | 'initialization' | 'review_submit' | 'verification_progress' | 'welcome' | 'email_verification' | 'phone_verification' | 'personal_information' | 'additional_documents';\n order: number;\n templateId?: number;\n labels: LocalizedText | Record<string, LocalizedText>;\n instructions: LocalizedText | Record<string, LocalizedText>;\n ui: ComponentUI | Record<string, ComponentUI>;\n config: ComponentConfig;\n}\n\n// Template KYC complet\nexport interface KYCTemplate {\n id: number;\n name: LocalizedText;\n description: LocalizedText;\n version: string;\n components: TemplateComponent[];\n}\n\n// État pour le template KYC\nexport interface TemplateState {\n template: KYCTemplate;\n currentComponentIndex: number;\n completedComponents: number[];\n componentData: Record<number, any>;\n errors: Record<number, string>;\n isProcessing: boolean;\n currentLanguage: string;\n showCustomStepper: boolean;\n session: SessionState;\n verification: VerificationState;\n}\n\nexport interface SessionState {\n session_id: string;\n token: string;\n isInitialized: boolean;\n isProcessing: boolean;\n error: string | null;\n /** True once loadSessionData has run (for resume flow); allows UI to show loading until country/data restored */\n sessionDataRestored?: boolean;\n}\n\nexport type VerificationStatus = 'idle' | 'in_progress' | 'success' | 'failed';\n\n\n\nexport interface VerificationState {\n status: VerificationStatus;\n result?: VerificationResult;\n}\n\n// Actions pour le template KYC\nexport interface TemplateActions {\n initializeTemplate: (template: KYCTemplate) => void;\n nextComponent: (overrideData?: any) => void;\n previousComponent: () => void;\n goToComponent: (componentId: number) => void;\n updateComponentData: (componentId: number, data: any) => void;\n validateComponent: (componentId: number, dataOverride?: any) => boolean;\n submitTemplate: () => Promise<void>;\n resetTemplate: () => void;\n restartSession: () => void;\n setLanguage: (language: string) => void;\n showCustomStepper: (show: boolean) => void;\n setVerificationState: (state: VerificationState) => void;\n submitVerification: () => Promise<void>;\n}\n\n// Hook pour le template KYC (return type of useTemplateKYCFlow)\nexport interface UseTemplateReturn {\n state: TemplateState;\n actions: TemplateActions;\n currentComponent: TemplateComponent | null;\n progress: number;\n canGoNext: boolean;\n canGoPrevious: boolean;\n isComplete: boolean;\n getLocalizedText: (text: LocalizedText) => string;\n initializeSession: () => Promise<void>;\n env: 'PRODUCTION' | 'SANDBOX';\n /** Optional API key for backend auth (e.g. email verification endpoints). */\n apiKey?: string;\n}\n\n\nexport interface ISilentCaptureResult {\n success: boolean;\n path?: string;\n mrz?: string;\n barcode?: string | any; \n error?: string;\n isAnalyzing?: boolean;\n bbox?: IBbox;\n country?: string;\n documentType?: string;\n templatePath?: string;\n lpipsScore?: number;\n}\n\nexport interface IBbox {\n minX: number;\n minY: number;\n width: number;\n height: number;\n}\n\nexport interface ICountryMapping {\n py_file?: string;\n front?: string;\n back?: string;\n keys_found?: string | null;\n mrz_type?: string;\n}\n\nexport interface Country {\n code?: string; // ISO 3166-1 alpha-2\n name: string;\n name_en: string;\n flag: string;\n regionMapping?: ICountryMapping;\n hasRegions?: boolean;\n}\n\n// ===== TYPES FOR BACKEND TEMPLATE FORMAT =====\n\n// Backend component configuration types\nexport interface WelcomeConfig {\n subtitle?: string;\n buttonText?: string;\n requirements?: string[];\n estimatedTime?: string;\n consentOptions?: {\n showPrivacyPolicy?: boolean;\n showTermsOfService?: boolean;\n showMarketingConsent?: boolean;\n };\n welcomeMessage?: string;\n showEstimatedTime?: boolean;\n}\n\nexport interface LocationCaptureConfig {\n showMap?: boolean;\n accuracy?: string;\n required?: boolean;\n maxDistance?: number;\n captureMethod?: string;\n allowManualEntry?: boolean;\n countryRestrictions?: string[];\n}\n\nexport interface GovernmentIdConfig {\n required?: boolean;\n allowSkip?: boolean;\n cameraOverlay?: {\n bbox?: {\n xMax?: number;\n xMin?: number;\n yMax?: number;\n yMin?: number;\n borderColor?: string;\n borderWidth?: number;\n cornerRadius?: number;\n };\n };\n documentTypes?: string[];\n requiredSides?: string;\n cameraSettings?: {\n quality?: number;\n flashMode?: string;\n maxRetakes?: number;\n allowRetake?: boolean;\n aspectRatio?: number;\n };\n validationRules?: string[];\n regionsByCountry?: Record<string, any>;\n selectedCountries?: string[];\n authenticationMethods?: Record<string, Record<string, {\n back?: string[];\n front?: string[];\n mrzTypes?: string[];\n }>>;\n documentTypesByCountry?: Record<string, string[]>;\n instructionsByDocumentType?: Record<string, LocalizedText>;\n}\n\nexport interface SelfieCaptureConfig {\n timeout?: number;\n maxAttempts?: number;\n faceMatching?: boolean;\n captureMethod?: string;\n qualityChecks?: string[];\n livenessChecks?: string[];\n matchThreshold?: string;\n livenessEnabled?: boolean;\n}\n\nexport interface ReviewSubmitConfig {\n allowEditing?: boolean;\n displayOptions?: string[];\n requireConsent?: boolean;\n postSubmitActions?: string[];\n}\n\nexport type BackendComponentConfig =\n | WelcomeConfig\n | LocationCaptureConfig\n | GovernmentIdConfig\n | SelfieCaptureConfig\n | ReviewSubmitConfig\n | Record<string, any>;\n\n// Backend component translation structure\nexport interface BackendComponentTranslations {\n en?: {\n title?: string;\n description?: string;\n instructions?: string;\n buttonText?: string;\n consentText?: string;\n successMessage?: string;\n };\n fr?: {\n title?: string;\n description?: string;\n instructions?: string;\n buttonText?: string;\n consentText?: string;\n successMessage?: string;\n };\n [key: string]: {\n title?: string;\n description?: string;\n instructions?: string;\n buttonText?: string;\n consentText?: string;\n successMessage?: string;\n } | undefined;\n}\n\n// Backend template component\nexport interface BackendTemplateComponent {\n id: string;\n type: 'welcome' | 'location-capture' | 'government-id' | 'selfie-capture' | 'review-submit' | 'verification-progress' | 'email-verification' | 'phone-verification' | 'personal-information' | 'additional-documents';\n order: number;\n config: BackendComponentConfig;\n required?: boolean;\n translations: BackendComponentTranslations;\n}\n\n// Backend template structure\nexport interface BackendKYCTemplate {\n id: string;\n name: {\n en: string;\n fr: string;\n [key: string]: string;\n };\n status?: string;\n version?: string;\n components: BackendTemplateComponent[];\n description?: {\n en: string;\n fr: string;\n [key: string]: string;\n };\n}\n\n// ===== NEW CONFIG TYPES FOR INPUT VERIFICATION =====\n\nexport interface EmailVerificationConfig {\n allowResend: boolean;\n otpSettings: {\n codeLength: number;\n expiryTime: number;\n };\n resendCooldown: number;\n emailValidation: {\n corporateOnly: boolean;\n validateFormat: boolean;\n checkDisposable: boolean;\n checkDeliverability: boolean;\n };\n verificationMethod: string;\n required?: boolean;\n}\n\nexport interface PhoneVerificationConfig {\n required: boolean;\n codeExpiry: number;\n codeLength: number;\n maxAttempts: number;\n resendDelay: number;\n countryRestrictions: string;\n verificationMethods: string[];\n allowSkipForVerified: boolean;\n}\n\nexport interface PersonalInfoField {\n id: string;\n type: string;\n label: string;\n required: boolean;\n placeholder?: string;\n autoFillFromId?: boolean;\n minAge?: number;\n verifyAge?: boolean;\n dateFormat?: string;\n}\n\nexport interface PersonalInformationConfig {\n fields: PersonalInfoField[];\n validationSettings: {\n validateOnBlur: boolean;\n showAllErrorsAtOnce: boolean;\n preventSubmissionWithErrors: boolean;\n };\n required?: boolean;\n}\n\nexport interface DocumentCategory {\n enabled: boolean;\n category: string;\n documentTypes: string[];\n}\n\nexport interface AdditionalDocumentsConfig {\n required: boolean;\n maxSizeEach: string;\n maxDocuments: number;\n minDocuments: number;\n documentCategories: DocumentCategory[];\n customDocumentTypes: any[];\n showBasedOnUserProfile: boolean;\n}\n\n// API response for template\nexport interface TemplateAPIResponse {\n id: string;\n name: {\n en: string;\n fr: string;\n [key: string]: string;\n };\n status?: string;\n version?: string;\n components: BackendTemplateComponent[];\n description?: {\n en: string;\n fr: string;\n [key: string]: string;\n };\n}\n\n// ===== NEW CONFIG TYPES FOR INPUT VERIFICATION =====\n\nexport interface EmailVerificationConfig {\n allowResend: boolean;\n otpSettings: {\n codeLength: number;\n expiryTime: number;\n };\n resendCooldown: number;\n emailValidation: {\n corporateOnly: boolean;\n validateFormat: boolean;\n checkDisposable: boolean;\n checkDeliverability: boolean;\n };\n verificationMethod: string;\n required?: boolean;\n}\n\nexport interface PhoneVerificationConfig {\n required: boolean;\n codeExpiry: number;\n codeLength: number;\n maxAttempts: number;\n resendDelay: number;\n countryRestrictions: string;\n verificationMethods: string[];\n allowSkipForVerified: boolean;\n}\n\nexport interface PersonalInfoField {\n id: string;\n type: string;\n label: string;\n required: boolean;\n placeholder?: string;\n autoFillFromId?: boolean;\n minAge?: number;\n verifyAge?: boolean;\n dateFormat?: string;\n}\n\nexport interface PersonalInformationConfig {\n fields: PersonalInfoField[];\n validationSettings: {\n validateOnBlur: boolean;\n showAllErrorsAtOnce: boolean;\n preventSubmissionWithErrors: boolean;\n };\n required?: boolean;\n}\n\nexport interface DocumentCategory {\n enabled: boolean;\n category: string;\n documentTypes: string[];\n}\n\nexport interface AdditionalDocumentsConfig {\n required: boolean;\n maxSizeEach: string;\n maxDocuments: number;\n minDocuments: number;\n documentCategories: DocumentCategory[];\n customDocumentTypes: any[];\n showBasedOnUserProfile: boolean;\n}"]}
|
package/package.json
CHANGED
|
@@ -464,8 +464,7 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({ component, value =
|
|
|
464
464
|
|
|
465
465
|
const handleExitKYCFlow = () => {
|
|
466
466
|
clearAuthToken();
|
|
467
|
-
|
|
468
|
-
// 2. Clear all local camera/capture states
|
|
467
|
+
// 1. Clear local UI and camera states immediately
|
|
469
468
|
setCapturedImages({});
|
|
470
469
|
if (onValueChange) onValueChange({}); // Clear the parent's form state
|
|
471
470
|
setCurrentSide('front');
|
|
@@ -476,14 +475,16 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({ component, value =
|
|
|
476
475
|
setIsProcessingCapture(false);
|
|
477
476
|
setProcessingImagePath(null);
|
|
478
477
|
|
|
479
|
-
//
|
|
480
|
-
if (actions.
|
|
481
|
-
// This is
|
|
478
|
+
// 2. Restart the backend session and context state
|
|
479
|
+
if (actions.restartSession) {
|
|
480
|
+
// This is your new custom action that wipes the token, state, and fetches a new session
|
|
481
|
+
actions.restartSession();
|
|
482
|
+
} else if (actions.resetTemplate) {
|
|
483
|
+
// Fallback
|
|
484
|
+
clearAuthToken();
|
|
482
485
|
actions.resetTemplate();
|
|
483
|
-
} else if (actions.goToComponent && state.template?.components?.length > 0) {
|
|
484
|
-
actions.goToComponent(state.template.components[0].id);
|
|
485
486
|
} else {
|
|
486
|
-
console.warn("Could not restart flow.
|
|
487
|
+
console.warn("Could not restart flow. Ensure restartSession is added to your KYC context actions.");
|
|
487
488
|
}
|
|
488
489
|
};
|
|
489
490
|
|
|
@@ -626,27 +627,72 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({ component, value =
|
|
|
626
627
|
let hintMessage = '';
|
|
627
628
|
|
|
628
629
|
// 2. Business Logic Errors
|
|
629
|
-
if (
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
630
|
+
if (
|
|
631
|
+
rawMessage.includes('card_not_fully_in_frame') ||
|
|
632
|
+
rawMessage.includes('non entièrement visible') ||
|
|
633
|
+
rawMessage.includes('aucun visage détecté') ||
|
|
634
|
+
rawMessage.includes('stabiliser la caméra')
|
|
635
|
+
) {
|
|
636
|
+
hintMessage = state.currentLanguage === 'en'
|
|
637
|
+
? 'Ensure the entire card and your face are clearly inside the frame.'
|
|
638
|
+
: 'Assurez-vous que toute la carte et le visage sont clairement dans le cadre.';
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
// 2. Document/Country Mismatch
|
|
642
|
+
// Catches: 'pays sélectionné', 'LPIPS'
|
|
643
|
+
else if (rawMessage.includes('pays sélectionné')) {
|
|
644
|
+
hintMessage = state.currentLanguage === 'en'
|
|
645
|
+
? 'This document does not match the selected country.'
|
|
646
|
+
: 'Ce document ne correspond pas au pays sélectionné.';
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
// 3. Front/Back Document Mismatch
|
|
650
|
+
// Catches: 'ne correspond pas au recto', 'document différent'
|
|
651
|
+
else if (rawMessage.includes('ne correspond pas au recto') || rawMessage.includes('document différent')) {
|
|
652
|
+
hintMessage = state.currentLanguage === 'en'
|
|
653
|
+
? 'Please scan the back of the exact same ID.'
|
|
654
|
+
: 'Veuillez scanner le verso de la même pièce d\'identité.';
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
// 4. Data Extraction Failures (OCR & MRZ)
|
|
658
|
+
// Catches: 'impossible de lire', 'impossible d'extraire', 'mrz illisible', 'lecture mrz invalide'
|
|
659
|
+
else if (
|
|
660
|
+
rawMessage.includes('impossible de lire') ||
|
|
661
|
+
rawMessage.includes('impossible d\'extraire') ||
|
|
662
|
+
rawMessage.includes('mrz') ||
|
|
663
|
+
rawMessage.includes('extraction') ||
|
|
664
|
+
rawMessage.includes('illisible') ||
|
|
665
|
+
rawMessage.includes('invalide')
|
|
666
|
+
) {
|
|
667
|
+
hintMessage = state.currentLanguage === 'en'
|
|
668
|
+
? 'Cannot read document text. Avoid glare, improve lighting, and hold steady.'
|
|
669
|
+
: 'Impossible de lire le texte. Évitez les reflets, améliorez l\'éclairage et restez immobile.';
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
// 5. Server Infrastructure & Downtime
|
|
673
|
+
// Catches: Cloudflare tunnel errors, 502/503 Gateway timeouts
|
|
674
|
+
else if (
|
|
675
|
+
rawMessage.includes('cloudflare') ||
|
|
676
|
+
rawMessage.includes('tunnel') ||
|
|
677
|
+
rawMessage.includes('502') ||
|
|
678
|
+
rawMessage.includes('503')
|
|
679
|
+
) {
|
|
680
|
+
hintMessage = state.currentLanguage === 'en'
|
|
681
|
+
? 'Server is temporarily offline. Please try again in a few minutes.'
|
|
682
|
+
: 'Le serveur est temporairement hors ligne. Veuillez réessayer dans quelques minutes.';
|
|
637
683
|
}
|
|
684
|
+
|
|
685
|
+
// 6. True Network Errors vs. Fallback Processing
|
|
638
686
|
else {
|
|
639
|
-
|
|
687
|
+
// Strictly detect Axios network drops without swallowing unknown custom errors
|
|
688
|
+
const isNetworkError = error?.isAxiosError || rawMessage === 'network error' || error?.code === 'econnaborted' || rawMessage.includes('network');
|
|
640
689
|
|
|
641
690
|
if (isNetworkError) {
|
|
642
691
|
hintMessage = state.currentLanguage === 'en'
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
}
|
|
646
|
-
hintMessage = state.currentLanguage === 'en' ? 'Analyzing...' : 'Analyse en cours...';
|
|
647
|
-
}
|
|
692
|
+
? 'Poor connection. Please check your internet.'
|
|
693
|
+
: 'Connexion faible. Veuillez vérifier votre internet.';
|
|
694
|
+
}
|
|
648
695
|
}
|
|
649
|
-
|
|
650
696
|
setSilentCaptureResult(prev => ({ ...prev, isAnalyzing: false, success: false, error: hintMessage }));
|
|
651
697
|
}
|
|
652
698
|
}
|
|
@@ -972,7 +1018,35 @@ const styles = StyleSheet.create({
|
|
|
972
1018
|
sideTitle: { fontSize: 22, fontWeight: 'bold', color: '#000', marginBottom: 12, textAlign: 'center' },
|
|
973
1019
|
imagePreviewWrapper: { width: '100%', borderRadius: 12, padding: 1, overflow: 'hidden', shadowColor: '#000', shadowOffset: { width: 0, height: 4 }, shadowOpacity: 0.12, shadowRadius: 8, elevation: 8, backgroundColor: '#f0f0f0', ...Platform.select({ web: { aspectRatio: 1.59, height: 'auto', maxWidth: 450, alignSelf: 'center' } as any, default: { height: 220 } }) },
|
|
974
1020
|
previewImage: { width: '100%', height: '100%', borderRadius: 12, resizeMode: 'cover' },
|
|
975
|
-
floatingErrorBanner: {
|
|
1021
|
+
floatingErrorBanner: {
|
|
1022
|
+
backgroundColor: '#FEF2F2',
|
|
1023
|
+
borderWidth: 1,
|
|
1024
|
+
borderColor: '#FCA5A5',
|
|
1025
|
+
paddingVertical: 12,
|
|
1026
|
+
paddingHorizontal: 16,
|
|
1027
|
+
borderRadius: 12,
|
|
1028
|
+
alignItems: 'center',
|
|
1029
|
+
justifyContent: 'center',
|
|
1030
|
+
...Platform.select({
|
|
1031
|
+
web: {
|
|
1032
|
+
position: 'absolute',
|
|
1033
|
+
bottom: '100%',
|
|
1034
|
+
marginBottom: 16,
|
|
1035
|
+
maxWidth: 500,
|
|
1036
|
+
width: '100%', // Safe to keep 100% on web since left/right aren't used
|
|
1037
|
+
alignSelf: 'center',
|
|
1038
|
+
zIndex: 100000,
|
|
1039
|
+
boxShadow: '0 4px 12px rgba(0,0,0,0.15)'
|
|
1040
|
+
} as any,
|
|
1041
|
+
default: {
|
|
1042
|
+
position: 'absolute',
|
|
1043
|
+
top: Platform.OS === 'ios' ? 90 : 70,
|
|
1044
|
+
left: 24,
|
|
1045
|
+
right: 24,
|
|
1046
|
+
zIndex: 10000
|
|
1047
|
+
}
|
|
1048
|
+
})
|
|
1049
|
+
},
|
|
976
1050
|
floatingErrorText: { color: '#991B1B', fontSize: 14, fontWeight: '700', textAlign: 'center' },
|
|
977
1051
|
processingOverlay: { flex: 1, backgroundColor: 'rgba(0, 0, 0, 0.7)', justifyContent: 'center', alignItems: 'center', zIndex: 9999, ...Platform.select({ web: { backdropFilter: 'blur(4px)' } as any }) },
|
|
978
1052
|
processingText: { color: '#FFF', fontSize: 18, fontWeight: 'bold', marginTop: 16, textAlign: 'center' },
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import React, { useEffect, useRef } from 'react';
|
|
2
|
-
import { View, Text, StyleSheet, TouchableOpacity } from 'react-native';
|
|
1
|
+
import React, { useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { View, Text, StyleSheet, TouchableOpacity, ActivityIndicator } from 'react-native';
|
|
3
3
|
import { TemplateComponent } from '../../types/KYC.types';
|
|
4
4
|
import { useTemplateKYCFlowContext } from '../../hooks/useTemplateKYCFlow';
|
|
5
5
|
import { useI18n } from '../../hooks/useI18n';
|
|
@@ -14,29 +14,34 @@ interface VerificationProgressTemplateProps {
|
|
|
14
14
|
language?: string;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
const MAX_RETRIES = 40; // 40 attempts * 3 seconds = 2 minutes of polling
|
|
18
|
+
|
|
17
19
|
export const VerificationProgressTemplate: React.FC<VerificationProgressTemplateProps> = () => {
|
|
18
20
|
const { t } = useI18n();
|
|
19
21
|
const { state, actions } = useTemplateKYCFlowContext();
|
|
20
22
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
+
const [retryCount, setRetryCount] = useState(0);
|
|
24
|
+
|
|
25
|
+
const timeoutIdRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
26
|
+
const isPollingRef = useRef(false);
|
|
23
27
|
const isMountedRef = useRef(true);
|
|
24
28
|
|
|
25
29
|
const verification = state.verification;
|
|
26
30
|
const isSuccess = verification.status === 'success';
|
|
27
31
|
const isFailed = verification.status === 'failed';
|
|
28
32
|
|
|
29
|
-
const getVerificationResult = async () => {
|
|
30
|
-
// If component unmounted while waiting, stop everything
|
|
33
|
+
const getVerificationResult = async (currentRetryCount = 0) => {
|
|
31
34
|
if (!isMountedRef.current) return;
|
|
32
35
|
|
|
33
36
|
try {
|
|
34
|
-
logger.log(`Starting
|
|
37
|
+
logger.log(`Starting verification check (attempt ${currentRetryCount + 1}/${MAX_RETRIES})...`);
|
|
35
38
|
const result: any = await kycService.getVerificationResult(state.session.session_id);
|
|
36
|
-
|
|
39
|
+
|
|
40
|
+
// 🚨 FIX 1: Clean the root status by removing trailing periods (e.g., "Pending." -> "pending")
|
|
41
|
+
const rootStatus = (result?.status || '').toLowerCase().trim().replace(/\.$/, '');
|
|
37
42
|
|
|
38
|
-
|
|
39
|
-
if (rootStatus === 'failed
|
|
43
|
+
// FAILED CONDITION
|
|
44
|
+
if (rootStatus === 'failed' || rootStatus === 'rejected' || rootStatus === 'error') {
|
|
40
45
|
actions.setVerificationState({
|
|
41
46
|
status: 'failed',
|
|
42
47
|
result: {
|
|
@@ -44,10 +49,10 @@ export const VerificationProgressTemplate: React.FC<VerificationProgressTemplate
|
|
|
44
49
|
issues: [result?.message || result?.reason || 'Verification failed. Sent for manual review.']
|
|
45
50
|
} as any
|
|
46
51
|
});
|
|
52
|
+
isPollingRef.current = false;
|
|
47
53
|
return;
|
|
48
54
|
}
|
|
49
55
|
|
|
50
|
-
// 2. Safely Extract Nested Payload
|
|
51
56
|
let vData = result?.data || result;
|
|
52
57
|
|
|
53
58
|
if (typeof vData === 'string') {
|
|
@@ -66,19 +71,51 @@ export const VerificationProgressTemplate: React.FC<VerificationProgressTemplate
|
|
|
66
71
|
throw new Error('Verification payload missing or invalid for current session');
|
|
67
72
|
}
|
|
68
73
|
|
|
69
|
-
const vStatus = (vData.verification_status || vData.status || vData.state || '').toLowerCase();
|
|
74
|
+
const vStatus = (vData.verification_status || vData.status || vData.state || '').toLowerCase().replace(/\.$/, '');
|
|
70
75
|
const vDecision = (vData.decision || vData.outcome || vData.result || '').toLowerCase();
|
|
71
|
-
|
|
76
|
+
|
|
77
|
+
logger.log(`Parsed Root Status: [${rootStatus}], Inner Status: [${vStatus}], Decision: [${vDecision}]`);
|
|
72
78
|
|
|
73
|
-
|
|
79
|
+
// 🚨 FIX 2: PENDING/PROCESSING CONDITION MOVED ABOVE SUCCESS
|
|
80
|
+
// If the server explicitly says it's pending, we poll. This prevents false "completed" triggers.
|
|
81
|
+
if (
|
|
82
|
+
rootStatus === 'pending' || rootStatus === 'processing' || rootStatus === 'in_progress' ||
|
|
83
|
+
vStatus === 'processing' || vStatus === 'pending' || vStatus === 'in_progress' || vStatus === 'initiated' || !vStatus
|
|
84
|
+
) {
|
|
85
|
+
if (currentRetryCount < MAX_RETRIES) {
|
|
86
|
+
const nextCount = currentRetryCount + 1;
|
|
87
|
+
if (isMountedRef.current) {
|
|
88
|
+
setRetryCount(nextCount);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
logger.log(`Verification still processing, scheduling next check in 3s...`);
|
|
92
|
+
if (timeoutIdRef.current) clearTimeout(timeoutIdRef.current);
|
|
93
|
+
|
|
94
|
+
timeoutIdRef.current = setTimeout(() => {
|
|
95
|
+
getVerificationResult(nextCount);
|
|
96
|
+
}, 3000);
|
|
97
|
+
} else {
|
|
98
|
+
logger.log('Max retries reached. Sending to manual review.');
|
|
99
|
+
actions.setVerificationState({
|
|
100
|
+
status: 'failed',
|
|
101
|
+
result: {
|
|
102
|
+
referenceId: state.session.session_id,
|
|
103
|
+
issues: ['Verification is taking longer than usual and requires manual review. Please check back later.']
|
|
104
|
+
} as any
|
|
105
|
+
});
|
|
106
|
+
isPollingRef.current = false;
|
|
107
|
+
}
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
74
110
|
|
|
75
|
-
// SUCCESS CONDITION
|
|
111
|
+
// SUCCESS CONDITION (Only runs if we are absolutely sure it is no longer pending)
|
|
76
112
|
if (
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
113
|
+
rootStatus === 'success' || rootStatus === 'approved' || rootStatus === 'verified' ||
|
|
114
|
+
vStatus === 'approved' || vStatus === 'verified' || vStatus === 'success' || vStatus === 'completed' ||
|
|
115
|
+
vDecision === 'approved' || vDecision === 'passed' || vDecision === 'clear'
|
|
80
116
|
) {
|
|
81
117
|
const hasActualData = vData && Object.keys(vData).length > 0;
|
|
118
|
+
|
|
82
119
|
if (!hasActualData) {
|
|
83
120
|
actions.setVerificationState({
|
|
84
121
|
status: 'failed',
|
|
@@ -87,6 +124,7 @@ export const VerificationProgressTemplate: React.FC<VerificationProgressTemplate
|
|
|
87
124
|
issues: ['Document text could not be read clearly. Please retake the photo in better lighting.']
|
|
88
125
|
} as any
|
|
89
126
|
});
|
|
127
|
+
isPollingRef.current = false;
|
|
90
128
|
return;
|
|
91
129
|
}
|
|
92
130
|
|
|
@@ -94,27 +132,12 @@ export const VerificationProgressTemplate: React.FC<VerificationProgressTemplate
|
|
|
94
132
|
status: 'success',
|
|
95
133
|
result: { ...result, referenceId: vData.verification_id || vData.session_id || state.session.session_id } as any
|
|
96
134
|
});
|
|
97
|
-
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// PENDING/PROCESSING CONDITION - NO MORE POLLING
|
|
101
|
-
if (
|
|
102
|
-
vStatus === 'processing' || vStatus === 'pending' || vStatus === 'in_progress' ||
|
|
103
|
-
vStatus === 'initiated' || (vStatus === 'completed' && selfieMatch === null) || !vStatus
|
|
104
|
-
) {
|
|
105
|
-
logger.log('Verification still processing. Sending to manual review.');
|
|
106
|
-
actions.setVerificationState({
|
|
107
|
-
status: 'failed',
|
|
108
|
-
result: {
|
|
109
|
-
referenceId: state.session.session_id,
|
|
110
|
-
issues: ['Verification requires manual review or is taking longer than usual. Please check back later.']
|
|
111
|
-
} as any
|
|
112
|
-
});
|
|
135
|
+
isPollingRef.current = false;
|
|
113
136
|
return;
|
|
114
137
|
}
|
|
115
138
|
|
|
116
139
|
// UNKNOWN FALLBACK
|
|
117
|
-
logger.warn(`Unknown verification status received: ${vStatus}`);
|
|
140
|
+
logger.warn(`Unknown verification status received: Root: ${rootStatus}, Inner: ${vStatus}`);
|
|
118
141
|
actions.setVerificationState({
|
|
119
142
|
status: 'failed',
|
|
120
143
|
result: {
|
|
@@ -122,37 +145,48 @@ export const VerificationProgressTemplate: React.FC<VerificationProgressTemplate
|
|
|
122
145
|
issues: ['Received an unknown status from the server.']
|
|
123
146
|
} as any
|
|
124
147
|
});
|
|
148
|
+
isPollingRef.current = false;
|
|
125
149
|
|
|
126
150
|
} catch (error: any) {
|
|
127
151
|
logger.error('getVerificationResult error', JSON.stringify(errorMessage(error) || { message: error?.message || 'Unknown error' }, null, 2));
|
|
128
152
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
153
|
+
if (currentRetryCount < MAX_RETRIES) {
|
|
154
|
+
const nextCount = currentRetryCount + 1;
|
|
155
|
+
if (isMountedRef.current) setRetryCount(nextCount);
|
|
156
|
+
|
|
157
|
+
if (timeoutIdRef.current) clearTimeout(timeoutIdRef.current);
|
|
158
|
+
timeoutIdRef.current = setTimeout(() => {
|
|
159
|
+
getVerificationResult(nextCount);
|
|
160
|
+
}, 3000);
|
|
161
|
+
} else {
|
|
162
|
+
actions.setVerificationState({
|
|
163
|
+
status: 'failed',
|
|
164
|
+
result: {
|
|
165
|
+
referenceId: state.session.session_id,
|
|
166
|
+
issues: ['Connection failed multiple times. Please check your network and try again.']
|
|
167
|
+
} as any
|
|
168
|
+
});
|
|
169
|
+
isPollingRef.current = false;
|
|
170
|
+
}
|
|
136
171
|
}
|
|
137
172
|
};
|
|
138
173
|
|
|
139
|
-
// 1. Kick off single fetch when component mounts
|
|
140
174
|
useEffect(() => {
|
|
141
175
|
isMountedRef.current = true;
|
|
142
|
-
if (state.session.session_id && !isSuccess && !isFailed && !
|
|
143
|
-
|
|
144
|
-
getVerificationResult();
|
|
176
|
+
if (state.session.session_id && !isSuccess && !isFailed && !isPollingRef.current) {
|
|
177
|
+
isPollingRef.current = true;
|
|
178
|
+
getVerificationResult(0);
|
|
145
179
|
}
|
|
146
180
|
}, [state.session.session_id, isSuccess, isFailed]);
|
|
147
181
|
|
|
148
|
-
// 2. Strict cleanup on unmount ONLY
|
|
149
182
|
useEffect(() => {
|
|
150
183
|
return () => {
|
|
151
184
|
isMountedRef.current = false;
|
|
185
|
+
if (timeoutIdRef.current) clearTimeout(timeoutIdRef.current);
|
|
186
|
+
isPollingRef.current = false;
|
|
152
187
|
};
|
|
153
188
|
}, []);
|
|
154
189
|
|
|
155
|
-
// 3. Auto-submit on success
|
|
156
190
|
useEffect(() => {
|
|
157
191
|
let autoSubmitTimer: ReturnType<typeof setTimeout>;
|
|
158
192
|
if (isSuccess) {
|
|
@@ -220,7 +254,9 @@ export const VerificationProgressTemplate: React.FC<VerificationProgressTemplate
|
|
|
220
254
|
<View style={styles.container}>
|
|
221
255
|
<Text style={styles.title}>{t('kyc.verificationProgress.title')}</Text>
|
|
222
256
|
<Text style={styles.subtitle}>
|
|
223
|
-
{
|
|
257
|
+
{state.currentLanguage === 'en'
|
|
258
|
+
? `Checking verification status...`
|
|
259
|
+
: `Vérification du statut en cours...`}
|
|
224
260
|
</Text>
|
|
225
261
|
|
|
226
262
|
<View style={styles.timeline}>
|
|
@@ -245,9 +281,14 @@ export const VerificationProgressTemplate: React.FC<VerificationProgressTemplate
|
|
|
245
281
|
<Text style={styles.cardLine}>{t('kyc.verificationProgress.steps.analyzing')}</Text>
|
|
246
282
|
|
|
247
283
|
<Text style={styles.cardTitle}>{t('kyc.verificationProgress.title')}</Text>
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
284
|
+
|
|
285
|
+
<View style={styles.loadingRow}>
|
|
286
|
+
<ActivityIndicator size="small" color="#2DBD60" style={{ marginRight: 8 }} />
|
|
287
|
+
<Text style={styles.cardLine}>
|
|
288
|
+
{t('kyc.verificationProgress.status.processing')}...
|
|
289
|
+
<Text style={styles.retryText}> ({retryCount}/{MAX_RETRIES})</Text>
|
|
290
|
+
</Text>
|
|
291
|
+
</View>
|
|
251
292
|
</View>
|
|
252
293
|
</View>
|
|
253
294
|
);
|
|
@@ -269,6 +310,8 @@ const styles = StyleSheet.create({
|
|
|
269
310
|
card: { backgroundColor: 'white', padding: 16, borderRadius: 16, marginTop: 24 },
|
|
270
311
|
cardTitle: { fontSize: 16, color: '#6B7280', marginTop: 8 },
|
|
271
312
|
cardLine: { fontSize: 18, color: '#111827', fontWeight: '600', marginTop: 2 },
|
|
313
|
+
loadingRow: { flexDirection: 'row', alignItems: 'center', marginTop: 4 },
|
|
314
|
+
retryText: { fontSize: 14, color: '#9CA3AF', fontWeight: '500' },
|
|
272
315
|
button: { paddingVertical: 14, borderRadius: 10, alignItems: 'center', marginTop: 14 },
|
|
273
316
|
primary: { backgroundColor: '#2DBD60' },
|
|
274
317
|
buttonText: { color: 'white', fontSize: 16, fontWeight: '600' },
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { useState, useCallback, useMemo, createContext, useContext, ReactNode, useEffect, useRef } from 'react';
|
|
2
2
|
import { KYCTemplate, TemplateState, TemplateActions, UseTemplateReturn, TemplateComponent, GovernmentDocumentType, VerificationState } from '../types/KYC.types';
|
|
3
3
|
import { KycEnvironment } from '../types/env.types';
|
|
4
|
-
import kycService, { authentification, truncateFields } from '../modules/api/KYCService';
|
|
4
|
+
import kycService, { authentification, clearAuthToken, truncateFields } from '../modules/api/KYCService';
|
|
5
5
|
import useI18n from './useI18n';
|
|
6
6
|
import { logger } from '../utils/logger';
|
|
7
7
|
import { countryMapping } from '../config/region_mapping';
|
|
@@ -1083,6 +1083,24 @@ export const useTemplateKYCFlow = (
|
|
|
1083
1083
|
setState(buildInitialState());
|
|
1084
1084
|
}, [buildInitialState, apiKey]),
|
|
1085
1085
|
|
|
1086
|
+
restartSession: useCallback(async () => {
|
|
1087
|
+
// 1. Clear the cached token so the backend doesn't reject the new request
|
|
1088
|
+
// Make sure to import clearAuthToken at the top of your context file!
|
|
1089
|
+
clearAuthToken();
|
|
1090
|
+
|
|
1091
|
+
// 2. Wipe the local component and UI state
|
|
1092
|
+
setState(buildInitialState());
|
|
1093
|
+
|
|
1094
|
+
|
|
1095
|
+
try {
|
|
1096
|
+
await initializeSession();
|
|
1097
|
+
// OR
|
|
1098
|
+
} catch (error) {
|
|
1099
|
+
logger.error("Failed to fetch new session on restart", error);
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
}, [buildInitialState, apiKey, initializeSession]),
|
|
1103
|
+
|
|
1086
1104
|
// Changer la langue
|
|
1087
1105
|
setLanguage: useCallback((language: string) => {
|
|
1088
1106
|
setState(prev => ({
|