@sanctum-key/react-native-sdk 1.0.46 → 1.0.48
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 +85 -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 +49 -53
- 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 +99 -24
- package/src/components/KYCElements/VerificationProgressTemplate.tsx +52 -49
- 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,26 +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
|
}
|
|
638
|
-
|
|
684
|
+
|
|
685
|
+
// 6. True Network Errors vs. Fallback Processing
|
|
639
686
|
else {
|
|
640
|
-
|
|
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');
|
|
641
689
|
|
|
642
690
|
if (isNetworkError) {
|
|
643
|
-
hintMessage = state.currentLanguage === 'en'
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
}
|
|
691
|
+
hintMessage = state.currentLanguage === 'en'
|
|
692
|
+
? 'Poor connection. Please check your internet.'
|
|
693
|
+
: 'Connexion faible. Veuillez vérifier votre internet.';
|
|
694
|
+
}
|
|
647
695
|
}
|
|
648
|
-
|
|
649
696
|
setSilentCaptureResult(prev => ({ ...prev, isAnalyzing: false, success: false, error: hintMessage }));
|
|
650
697
|
}
|
|
651
698
|
}
|
|
@@ -971,7 +1018,35 @@ const styles = StyleSheet.create({
|
|
|
971
1018
|
sideTitle: { fontSize: 22, fontWeight: 'bold', color: '#000', marginBottom: 12, textAlign: 'center' },
|
|
972
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 } }) },
|
|
973
1020
|
previewImage: { width: '100%', height: '100%', borderRadius: 12, resizeMode: 'cover' },
|
|
974
|
-
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
|
+
},
|
|
975
1050
|
floatingErrorText: { color: '#991B1B', fontSize: 14, fontWeight: '700', textAlign: 'center' },
|
|
976
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 }) },
|
|
977
1052
|
processingText: { color: '#FFF', fontSize: 18, fontWeight: 'bold', marginTop: 16, textAlign: 'center' },
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { useEffect, useRef, useState } from 'react';
|
|
2
|
-
import { View, Text, StyleSheet, TouchableOpacity } from 'react-native';
|
|
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,16 +14,14 @@ interface VerificationProgressTemplateProps {
|
|
|
14
14
|
language?: string;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
const MAX_RETRIES = 40; // 40 attempts * 3 seconds =
|
|
17
|
+
const MAX_RETRIES = 40; // 40 attempts * 3 seconds = 2 minutes of polling
|
|
18
18
|
|
|
19
19
|
export const VerificationProgressTemplate: React.FC<VerificationProgressTemplateProps> = () => {
|
|
20
20
|
const { t } = useI18n();
|
|
21
21
|
const { state, actions } = useTemplateKYCFlowContext();
|
|
22
22
|
|
|
23
23
|
const [retryCount, setRetryCount] = useState(0);
|
|
24
|
-
const [isRetrying, setIsRetrying] = useState(false);
|
|
25
24
|
|
|
26
|
-
// 🚨 BULLETPROOF REFS: Prevents memory leaks and duplicate polling loops
|
|
27
25
|
const timeoutIdRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
28
26
|
const isPollingRef = useRef(false);
|
|
29
27
|
const isMountedRef = useRef(true);
|
|
@@ -33,14 +31,12 @@ export const VerificationProgressTemplate: React.FC<VerificationProgressTemplate
|
|
|
33
31
|
const isFailed = verification.status === 'failed';
|
|
34
32
|
|
|
35
33
|
const getVerificationResult = async (currentRetryCount = 0) => {
|
|
36
|
-
// If component unmounted while waiting, stop everything
|
|
37
34
|
if (!isMountedRef.current) return;
|
|
38
35
|
|
|
39
36
|
try {
|
|
40
|
-
logger.log(`Starting verification check (attempt ${currentRetryCount + 1}/${MAX_RETRIES})
|
|
37
|
+
logger.log(`Starting verification check (attempt ${currentRetryCount + 1}/${MAX_RETRIES})...`);
|
|
41
38
|
const result: any = await kycService.getVerificationResult(state.session.session_id);
|
|
42
|
-
|
|
43
|
-
|
|
39
|
+
|
|
44
40
|
const rootStatus = (result?.status || '').toLowerCase();
|
|
45
41
|
if (rootStatus === 'failed.' || rootStatus === 'failed' || rootStatus === 'rejected' || rootStatus === 'error') {
|
|
46
42
|
actions.setVerificationState({
|
|
@@ -50,12 +46,10 @@ export const VerificationProgressTemplate: React.FC<VerificationProgressTemplate
|
|
|
50
46
|
issues: [result?.message || result?.reason || 'Verification failed. Sent for manual review.']
|
|
51
47
|
} as any
|
|
52
48
|
});
|
|
53
|
-
if (isMountedRef.current) setIsRetrying(false);
|
|
54
49
|
isPollingRef.current = false;
|
|
55
50
|
return;
|
|
56
51
|
}
|
|
57
52
|
|
|
58
|
-
// 2. Safely Extract Nested Payload
|
|
59
53
|
let vData = result?.data || result;
|
|
60
54
|
|
|
61
55
|
if (typeof vData === 'string') {
|
|
@@ -76,17 +70,21 @@ export const VerificationProgressTemplate: React.FC<VerificationProgressTemplate
|
|
|
76
70
|
|
|
77
71
|
const vStatus = (vData.verification_status || vData.status || vData.state || '').toLowerCase();
|
|
78
72
|
const vDecision = (vData.decision || vData.outcome || vData.result || '').toLowerCase();
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
logger.log(`Parsed Status: [${vStatus}], Decision: [${vDecision}], Selfie Match: [${selfieMatch}]`);
|
|
73
|
+
|
|
74
|
+
logger.log(`Parsed Status: [${vStatus}], Decision: [${vDecision}]`);
|
|
82
75
|
|
|
83
76
|
// SUCCESS CONDITION
|
|
84
77
|
if (
|
|
85
|
-
vStatus === 'approved' ||
|
|
86
|
-
|
|
87
|
-
|
|
78
|
+
vStatus === 'approved' ||
|
|
79
|
+
vStatus === 'verified' ||
|
|
80
|
+
vStatus === 'success' ||
|
|
81
|
+
vStatus === 'completed' ||
|
|
82
|
+
vDecision === 'approved' ||
|
|
83
|
+
vDecision === 'passed' ||
|
|
84
|
+
vDecision === 'clear'
|
|
88
85
|
) {
|
|
89
86
|
const hasActualData = vData && Object.keys(vData).length > 0;
|
|
87
|
+
|
|
90
88
|
if (!hasActualData) {
|
|
91
89
|
actions.setVerificationState({
|
|
92
90
|
status: 'failed',
|
|
@@ -95,7 +93,6 @@ export const VerificationProgressTemplate: React.FC<VerificationProgressTemplate
|
|
|
95
93
|
issues: ['Document text could not be read clearly. Please retake the photo in better lighting.']
|
|
96
94
|
} as any
|
|
97
95
|
});
|
|
98
|
-
if (isMountedRef.current) setIsRetrying(false);
|
|
99
96
|
isPollingRef.current = false;
|
|
100
97
|
return;
|
|
101
98
|
}
|
|
@@ -104,38 +101,39 @@ export const VerificationProgressTemplate: React.FC<VerificationProgressTemplate
|
|
|
104
101
|
status: 'success',
|
|
105
102
|
result: { ...result, referenceId: vData.verification_id || vData.session_id || state.session.session_id } as any
|
|
106
103
|
});
|
|
107
|
-
if (isMountedRef.current) setIsRetrying(false);
|
|
108
104
|
isPollingRef.current = false;
|
|
109
105
|
return;
|
|
110
106
|
}
|
|
111
107
|
|
|
112
|
-
// PENDING/PROCESSING CONDITION
|
|
108
|
+
// PENDING/PROCESSING CONDITION - RE-INTRODUCING POLLING
|
|
113
109
|
if (
|
|
114
|
-
vStatus === 'processing' ||
|
|
115
|
-
vStatus === '
|
|
110
|
+
vStatus === 'processing' ||
|
|
111
|
+
vStatus === 'pending' ||
|
|
112
|
+
vStatus === 'in_progress' ||
|
|
113
|
+
vStatus === 'initiated' ||
|
|
114
|
+
!vStatus
|
|
116
115
|
) {
|
|
117
116
|
if (currentRetryCount < MAX_RETRIES) {
|
|
118
|
-
const
|
|
117
|
+
const nextCount = currentRetryCount + 1;
|
|
119
118
|
if (isMountedRef.current) {
|
|
120
|
-
|
|
121
|
-
setIsRetrying(true);
|
|
119
|
+
setRetryCount(nextCount);
|
|
122
120
|
}
|
|
123
|
-
logger.log(`Verification still processing, retrying in 3s... (${nextRetryCount}/${MAX_RETRIES})`);
|
|
124
121
|
|
|
122
|
+
logger.log(`Verification still processing, scheduling next check in 3s...`);
|
|
125
123
|
if (timeoutIdRef.current) clearTimeout(timeoutIdRef.current);
|
|
124
|
+
|
|
126
125
|
timeoutIdRef.current = setTimeout(() => {
|
|
127
|
-
getVerificationResult(
|
|
126
|
+
getVerificationResult(nextCount);
|
|
128
127
|
}, 3000);
|
|
129
128
|
} else {
|
|
130
|
-
logger.
|
|
129
|
+
logger.log('Max retries reached. Sending to manual review.');
|
|
131
130
|
actions.setVerificationState({
|
|
132
131
|
status: 'failed',
|
|
133
132
|
result: {
|
|
134
133
|
referenceId: state.session.session_id,
|
|
135
|
-
issues: ['Verification
|
|
134
|
+
issues: ['Verification is taking longer than usual and requires manual review. Please check back later.']
|
|
136
135
|
} as any
|
|
137
136
|
});
|
|
138
|
-
if (isMountedRef.current) setIsRetrying(false);
|
|
139
137
|
isPollingRef.current = false;
|
|
140
138
|
}
|
|
141
139
|
return;
|
|
@@ -143,26 +141,28 @@ export const VerificationProgressTemplate: React.FC<VerificationProgressTemplate
|
|
|
143
141
|
|
|
144
142
|
// UNKNOWN FALLBACK
|
|
145
143
|
logger.warn(`Unknown verification status received: ${vStatus}`);
|
|
146
|
-
|
|
144
|
+
actions.setVerificationState({
|
|
145
|
+
status: 'failed',
|
|
146
|
+
result: {
|
|
147
|
+
referenceId: state.session.session_id,
|
|
148
|
+
issues: ['Received an unknown status from the server.']
|
|
149
|
+
} as any
|
|
150
|
+
});
|
|
147
151
|
isPollingRef.current = false;
|
|
148
152
|
|
|
149
153
|
} catch (error: any) {
|
|
150
154
|
logger.error('getVerificationResult error', JSON.stringify(errorMessage(error) || { message: error?.message || 'Unknown error' }, null, 2));
|
|
151
155
|
|
|
156
|
+
// If network fails temporarily, we should also retry instead of instantly failing
|
|
152
157
|
if (currentRetryCount < MAX_RETRIES) {
|
|
153
|
-
const
|
|
154
|
-
if (isMountedRef.current)
|
|
155
|
-
setRetryCount(nextRetryCount);
|
|
156
|
-
setIsRetrying(true);
|
|
157
|
-
}
|
|
158
|
-
logger.log(`Error occurred, retrying in 3s... (${nextRetryCount}/${MAX_RETRIES})`);
|
|
158
|
+
const nextCount = currentRetryCount + 1;
|
|
159
|
+
if (isMountedRef.current) setRetryCount(nextCount);
|
|
159
160
|
|
|
160
161
|
if (timeoutIdRef.current) clearTimeout(timeoutIdRef.current);
|
|
161
162
|
timeoutIdRef.current = setTimeout(() => {
|
|
162
|
-
getVerificationResult(
|
|
163
|
+
getVerificationResult(nextCount);
|
|
163
164
|
}, 3000);
|
|
164
165
|
} else {
|
|
165
|
-
logger.error('Max retries reached after errors');
|
|
166
166
|
actions.setVerificationState({
|
|
167
167
|
status: 'failed',
|
|
168
168
|
result: {
|
|
@@ -170,22 +170,19 @@ export const VerificationProgressTemplate: React.FC<VerificationProgressTemplate
|
|
|
170
170
|
issues: ['Connection failed multiple times. Please check your network and try again.']
|
|
171
171
|
} as any
|
|
172
172
|
});
|
|
173
|
-
if (isMountedRef.current) setIsRetrying(false);
|
|
174
173
|
isPollingRef.current = false;
|
|
175
174
|
}
|
|
176
175
|
}
|
|
177
176
|
};
|
|
178
177
|
|
|
179
|
-
// 1. Kick off polling when component mounts
|
|
180
178
|
useEffect(() => {
|
|
181
179
|
isMountedRef.current = true;
|
|
182
180
|
if (state.session.session_id && !isSuccess && !isFailed && !isPollingRef.current) {
|
|
183
181
|
isPollingRef.current = true;
|
|
184
|
-
getVerificationResult(0);
|
|
182
|
+
getVerificationResult(0);
|
|
185
183
|
}
|
|
186
184
|
}, [state.session.session_id, isSuccess, isFailed]);
|
|
187
185
|
|
|
188
|
-
// 2. Strict cleanup on unmount ONLY
|
|
189
186
|
useEffect(() => {
|
|
190
187
|
return () => {
|
|
191
188
|
isMountedRef.current = false;
|
|
@@ -194,7 +191,6 @@ export const VerificationProgressTemplate: React.FC<VerificationProgressTemplate
|
|
|
194
191
|
};
|
|
195
192
|
}, []);
|
|
196
193
|
|
|
197
|
-
// 3. Auto-submit on success
|
|
198
194
|
useEffect(() => {
|
|
199
195
|
let autoSubmitTimer: ReturnType<typeof setTimeout>;
|
|
200
196
|
if (isSuccess) {
|
|
@@ -262,9 +258,9 @@ export const VerificationProgressTemplate: React.FC<VerificationProgressTemplate
|
|
|
262
258
|
<View style={styles.container}>
|
|
263
259
|
<Text style={styles.title}>{t('kyc.verificationProgress.title')}</Text>
|
|
264
260
|
<Text style={styles.subtitle}>
|
|
265
|
-
{
|
|
266
|
-
?
|
|
267
|
-
:
|
|
261
|
+
{state.currentLanguage === 'en'
|
|
262
|
+
? `Checking verification status...`
|
|
263
|
+
: `Vérification du statut en cours...`}
|
|
268
264
|
</Text>
|
|
269
265
|
|
|
270
266
|
<View style={styles.timeline}>
|
|
@@ -289,9 +285,14 @@ export const VerificationProgressTemplate: React.FC<VerificationProgressTemplate
|
|
|
289
285
|
<Text style={styles.cardLine}>{t('kyc.verificationProgress.steps.analyzing')}</Text>
|
|
290
286
|
|
|
291
287
|
<Text style={styles.cardTitle}>{t('kyc.verificationProgress.title')}</Text>
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
288
|
+
|
|
289
|
+
<View style={styles.loadingRow}>
|
|
290
|
+
<ActivityIndicator size="small" color="#2DBD60" style={{ marginRight: 8 }} />
|
|
291
|
+
<Text style={styles.cardLine}>
|
|
292
|
+
{t('kyc.verificationProgress.status.processing')}...
|
|
293
|
+
<Text style={styles.retryText}> ({retryCount}/{MAX_RETRIES})</Text>
|
|
294
|
+
</Text>
|
|
295
|
+
</View>
|
|
295
296
|
</View>
|
|
296
297
|
</View>
|
|
297
298
|
);
|
|
@@ -313,6 +314,8 @@ const styles = StyleSheet.create({
|
|
|
313
314
|
card: { backgroundColor: 'white', padding: 16, borderRadius: 16, marginTop: 24 },
|
|
314
315
|
cardTitle: { fontSize: 16, color: '#6B7280', marginTop: 8 },
|
|
315
316
|
cardLine: { fontSize: 18, color: '#111827', fontWeight: '600', marginTop: 2 },
|
|
317
|
+
loadingRow: { flexDirection: 'row', alignItems: 'center', marginTop: 4 },
|
|
318
|
+
retryText: { fontSize: 14, color: '#9CA3AF', fontWeight: '500' },
|
|
316
319
|
button: { paddingVertical: 14, borderRadius: 10, alignItems: 'center', marginTop: 14 },
|
|
317
320
|
primary: { backgroundColor: '#2DBD60' },
|
|
318
321
|
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 => ({
|