@transfergratis/react-native-sdk 0.1.22 → 0.1.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. package/android/src/main/AndroidManifest.xml +9 -4
  2. package/build/components/EnhancedCameraView.d.ts.map +1 -1
  3. package/build/components/EnhancedCameraView.js +26 -3
  4. package/build/components/EnhancedCameraView.js.map +1 -1
  5. package/build/components/EnhancedCameraView.web.d.ts.map +1 -1
  6. package/build/components/EnhancedCameraView.web.js +21 -0
  7. package/build/components/EnhancedCameraView.web.js.map +1 -1
  8. package/build/components/KYCElements/CameraCapture.d.ts.map +1 -1
  9. package/build/components/KYCElements/CameraCapture.js +4 -3
  10. package/build/components/KYCElements/CameraCapture.js.map +1 -1
  11. package/build/components/KYCElements/CountrySelectionTemplate.d.ts +5 -2
  12. package/build/components/KYCElements/CountrySelectionTemplate.d.ts.map +1 -1
  13. package/build/components/KYCElements/CountrySelectionTemplate.js +360 -101
  14. package/build/components/KYCElements/CountrySelectionTemplate.js.map +1 -1
  15. package/build/components/KYCElements/FileUpload.d.ts.map +1 -1
  16. package/build/components/KYCElements/FileUpload.js +5 -4
  17. package/build/components/KYCElements/FileUpload.js.map +1 -1
  18. package/build/components/KYCElements/FileUploadTemplate.d.ts.map +1 -1
  19. package/build/components/KYCElements/FileUploadTemplate.js +5 -4
  20. package/build/components/KYCElements/FileUploadTemplate.js.map +1 -1
  21. package/build/components/KYCElements/IDCardCapture.d.ts.map +1 -1
  22. package/build/components/KYCElements/IDCardCapture.js +193 -237
  23. package/build/components/KYCElements/IDCardCapture.js.map +1 -1
  24. package/build/components/KYCElements/LocationCaptureTemplate.d.ts.map +1 -1
  25. package/build/components/KYCElements/LocationCaptureTemplate.js +78 -37
  26. package/build/components/KYCElements/LocationCaptureTemplate.js.map +1 -1
  27. package/build/components/KYCElements/OrientationVideoCapture.js +3 -2
  28. package/build/components/KYCElements/OrientationVideoCapture.js.map +1 -1
  29. package/build/components/KYCElements/OrientationVideoCaptureEnhanced.js +3 -2
  30. package/build/components/KYCElements/OrientationVideoCaptureEnhanced.js.map +1 -1
  31. package/build/components/KYCElements/OrientationVideoCaptureFinal.js +3 -2
  32. package/build/components/KYCElements/OrientationVideoCaptureFinal.js.map +1 -1
  33. package/build/components/KYCElements/SelfieCapture.d.ts.map +1 -1
  34. package/build/components/KYCElements/SelfieCapture.js +4 -3
  35. package/build/components/KYCElements/SelfieCapture.js.map +1 -1
  36. package/build/components/KYCElements/SelfieCaptureTemplate.d.ts.map +1 -1
  37. package/build/components/KYCElements/SelfieCaptureTemplate.js +182 -39
  38. package/build/components/KYCElements/SelfieCaptureTemplate.js.map +1 -1
  39. package/build/components/KYCElements/WelcomeTemplate.d.ts +12 -0
  40. package/build/components/KYCElements/WelcomeTemplate.d.ts.map +1 -0
  41. package/build/components/KYCElements/WelcomeTemplate.js +243 -0
  42. package/build/components/KYCElements/WelcomeTemplate.js.map +1 -0
  43. package/build/components/TemplateKYCExample.d.ts +4 -2
  44. package/build/components/TemplateKYCExample.d.ts.map +1 -1
  45. package/build/components/TemplateKYCExample.js +5 -68
  46. package/build/components/TemplateKYCExample.js.map +1 -1
  47. package/build/components/TemplateKYCFlowRefactored.d.ts +2 -1
  48. package/build/components/TemplateKYCFlowRefactored.d.ts.map +1 -1
  49. package/build/components/TemplateKYCFlowRefactored.js +95 -9
  50. package/build/components/TemplateKYCFlowRefactored.js.map +1 -1
  51. package/build/components/example/DynamicTemplateExample.d.ts +10 -0
  52. package/build/components/example/DynamicTemplateExample.d.ts.map +1 -0
  53. package/build/components/example/DynamicTemplateExample.js +241 -0
  54. package/build/components/example/DynamicTemplateExample.js.map +1 -0
  55. package/build/config/allowedDomains.d.ts +30 -0
  56. package/build/config/allowedDomains.d.ts.map +1 -0
  57. package/build/config/allowedDomains.js +127 -0
  58. package/build/config/allowedDomains.js.map +1 -0
  59. package/build/hooks/useTemplateKYCFlow.d.ts.map +1 -1
  60. package/build/hooks/useTemplateKYCFlow.js +68 -43
  61. package/build/hooks/useTemplateKYCFlow.js.map +1 -1
  62. package/build/hooks/useTemplateLoader.d.ts +14 -0
  63. package/build/hooks/useTemplateLoader.d.ts.map +1 -0
  64. package/build/hooks/useTemplateLoader.js +85 -0
  65. package/build/hooks/useTemplateLoader.js.map +1 -0
  66. package/build/i18n/en/index.d.ts +9 -0
  67. package/build/i18n/en/index.d.ts.map +1 -1
  68. package/build/i18n/en/index.js +9 -0
  69. package/build/i18n/en/index.js.map +1 -1
  70. package/build/i18n/fr/index.d.ts +9 -0
  71. package/build/i18n/fr/index.d.ts.map +1 -1
  72. package/build/i18n/fr/index.js +9 -0
  73. package/build/i18n/fr/index.js.map +1 -1
  74. package/build/index.d.ts +5 -0
  75. package/build/index.d.ts.map +1 -1
  76. package/build/index.js +8 -0
  77. package/build/index.js.map +1 -1
  78. package/build/modules/api/CardAuthentification.js +1 -0
  79. package/build/modules/api/CardAuthentification.js.map +1 -1
  80. package/build/modules/api/KYCService.d.ts +4 -1
  81. package/build/modules/api/KYCService.d.ts.map +1 -1
  82. package/build/modules/api/KYCService.js +17 -5
  83. package/build/modules/api/KYCService.js.map +1 -1
  84. package/build/modules/api/TemplateService.d.ts +45 -0
  85. package/build/modules/api/TemplateService.d.ts.map +1 -0
  86. package/build/modules/api/TemplateService.js +145 -0
  87. package/build/modules/api/TemplateService.js.map +1 -0
  88. package/build/modules/api/types.d.ts +1 -0
  89. package/build/modules/api/types.d.ts.map +1 -1
  90. package/build/modules/api/types.js.map +1 -1
  91. package/build/types/KYC.types.d.ts +144 -4
  92. package/build/types/KYC.types.d.ts.map +1 -1
  93. package/build/types/KYC.types.js +15 -0
  94. package/build/types/KYC.types.js.map +1 -1
  95. package/build/utils/cropByObb.d.ts +1 -0
  96. package/build/utils/cropByObb.d.ts.map +1 -1
  97. package/build/utils/cropByObb.js +70 -0
  98. package/build/utils/cropByObb.js.map +1 -1
  99. package/build/utils/platformAlert.d.ts +20 -0
  100. package/build/utils/platformAlert.d.ts.map +1 -0
  101. package/build/utils/platformAlert.js +67 -0
  102. package/build/utils/platformAlert.js.map +1 -0
  103. package/build/utils/template-transformer.d.ts +10 -0
  104. package/build/utils/template-transformer.d.ts.map +1 -0
  105. package/build/utils/template-transformer.js +353 -0
  106. package/build/utils/template-transformer.js.map +1 -0
  107. package/build/web/WebKYCEntry.d.ts.map +1 -1
  108. package/build/web/WebKYCEntry.js +102 -20
  109. package/build/web/WebKYCEntry.js.map +1 -1
  110. package/package.json +1 -1
  111. package/src/components/EnhancedCameraView.tsx +31 -2
  112. package/src/components/EnhancedCameraView.web.tsx +24 -0
  113. package/src/components/KYCElements/CameraCapture.tsx +4 -3
  114. package/src/components/KYCElements/CountrySelectionTemplate.tsx +410 -113
  115. package/src/components/KYCElements/FileUpload.tsx +5 -4
  116. package/src/components/KYCElements/FileUploadTemplate.tsx +5 -4
  117. package/src/components/KYCElements/IDCardCapture.tsx +196 -254
  118. package/src/components/KYCElements/LocationCaptureTemplate.tsx +95 -44
  119. package/src/components/KYCElements/OrientationVideoCapture.tsx +2 -2
  120. package/src/components/KYCElements/OrientationVideoCaptureEnhanced.tsx +2 -2
  121. package/src/components/KYCElements/OrientationVideoCaptureFinal.tsx +2 -2
  122. package/src/components/KYCElements/SelfieCapture.tsx +4 -3
  123. package/src/components/KYCElements/SelfieCaptureTemplate.tsx +195 -41
  124. package/src/components/KYCElements/WelcomeTemplate.tsx +289 -0
  125. package/src/components/TemplateKYCExample.tsx +16 -71
  126. package/src/components/TemplateKYCFlowRefactored.tsx +121 -11
  127. package/src/components/example/DynamicTemplateExample.tsx +289 -0
  128. package/src/config/allowedDomains.ts +152 -0
  129. package/src/hooks/useTemplateKYCFlow.tsx +71 -46
  130. package/src/hooks/useTemplateLoader.ts +102 -0
  131. package/src/i18n/en/index.ts +10 -0
  132. package/src/i18n/fr/index.ts +9 -0
  133. package/src/index.ts +11 -0
  134. package/src/modules/api/CardAuthentification.ts +1 -1
  135. package/src/modules/api/KYCService.ts +18 -8
  136. package/src/modules/api/TemplateService.ts +167 -0
  137. package/src/modules/api/types.ts +1 -0
  138. package/src/types/KYC.types.ts +188 -3
  139. package/src/utils/cropByObb.ts +83 -3
  140. package/src/utils/platformAlert.ts +85 -0
  141. package/src/utils/template-transformer.ts +433 -0
  142. package/src/web/WebKYCEntry.tsx +122 -24
@@ -0,0 +1,167 @@
1
+ import axios from 'axios';
2
+ import { BackendKYCTemplate, TemplateAPIResponse } from '../../types/KYC.types';
3
+ import { logger } from '../../utils/logger';
4
+ import { errorMessage } from './KYCService';
5
+
6
+ export class TemplateServiceClass {
7
+ private backendServiceURL = 'https://service.sanctumkey.com/api/v1';
8
+ private cache: Map<string, { template: BackendKYCTemplate; timestamp: number }> = new Map();
9
+ private cacheTimeout = 5 * 60 * 1000; // 5 minutes
10
+
11
+ /**
12
+ * Get headers for API requests
13
+ */
14
+ private getHeaders(apiKey?: string, token?: string) {
15
+ const headers: Record<string, string> = {
16
+ 'Content-Type': 'application/json',
17
+ };
18
+
19
+ if (apiKey) {
20
+ headers['Authorization'] = `ApiKey ${apiKey}`;
21
+ } else if (token) {
22
+ headers['Authorization'] = `Bearer ${token}`;
23
+ }
24
+
25
+ return headers;
26
+ }
27
+
28
+ /**
29
+ * Fetch a template from the backend API
30
+ * @param templateId - The ID of the template to fetch
31
+ * @param apiKey - Optional API key for authentication
32
+ * @param token - Optional Bearer token for authentication
33
+ * @param useCache - Whether to use cached version if available (default: true)
34
+ * @returns The backend template
35
+ */
36
+ async fetchTemplate(
37
+ templateId: string,
38
+ apiKey?: string,
39
+ token?: string,
40
+ useCache: boolean = true
41
+ ): Promise<BackendKYCTemplate> {
42
+ // Check cache first
43
+ if (useCache) {
44
+ const cached = this.cache.get(templateId);
45
+ if (cached) {
46
+ const age = Date.now() - cached.timestamp;
47
+ if (age < this.cacheTimeout) {
48
+ logger.log(`Template ${templateId} loaded from cache`);
49
+ return cached.template;
50
+ } else {
51
+ // Cache expired, remove it
52
+ this.cache.delete(templateId);
53
+ }
54
+ }
55
+ }
56
+
57
+ try {
58
+ const url = `${this.backendServiceURL}/templates/kyc/template/${templateId}/`;
59
+ logger.log(`Fetching template from: ${url} with apiKey: ${apiKey} and token: ${token}`,{
60
+ headers: this.getHeaders(apiKey, token)
61
+ });
62
+
63
+ const response = await axios.get<TemplateAPIResponse>(url, {
64
+ headers: this.getHeaders(apiKey, token),
65
+ timeout: 30000, // 30 seconds timeout
66
+ });
67
+
68
+ const template: BackendKYCTemplate = response.data;
69
+
70
+ // Validate template structure
71
+ if (!template.id || !template.name || !Array.isArray(template.components)) {
72
+ throw new Error('Invalid template structure received from API');
73
+ }
74
+
75
+ // Store in cache
76
+ this.cache.set(templateId, {
77
+ template,
78
+ timestamp: Date.now(),
79
+ });
80
+
81
+ logger.log(`Template ${templateId} fetched successfully`);
82
+ return template;
83
+ } catch (error: any) {
84
+ logger.error(`Error fetching template ${templateId}:`, JSON.stringify(errorMessage(error), null, 2));
85
+
86
+ // Extract backend error message if available
87
+ const backendMessage = errorMessage(error);
88
+
89
+ if (error.response?.status === 404) {
90
+ throw new Error(`Template not found: ${templateId}`);
91
+ }
92
+
93
+ if (error.response?.status === 401 || error.response?.status === 403) {
94
+ throw new Error(`Unauthorized: Invalid API key or token`);
95
+ }
96
+
97
+ if (backendMessage) {
98
+ throw new Error(`Failed to fetch template: ${JSON.stringify(backendMessage)}`);
99
+ }
100
+
101
+ throw new Error(`Failed to fetch template: ${error.message || 'Unknown error'}`);
102
+ }
103
+ }
104
+
105
+ /**
106
+ * Clear the template cache
107
+ * @param templateId - Optional template ID to clear. If not provided, clears all cache.
108
+ */
109
+ clearCache(templateId?: string): void {
110
+ if (templateId) {
111
+ this.cache.delete(templateId);
112
+ logger.log(`Cache cleared for template: ${templateId}`);
113
+ } else {
114
+ this.cache.clear();
115
+ logger.log('All template cache cleared');
116
+ }
117
+ }
118
+
119
+ /**
120
+ * Get cached template if available
121
+ * @param templateId - The template ID
122
+ * @returns The cached template or null if not found/expired
123
+ */
124
+ getCachedTemplate(templateId: string): BackendKYCTemplate | null {
125
+ const cached = this.cache.get(templateId);
126
+ if (cached) {
127
+ const age = Date.now() - cached.timestamp;
128
+ if (age < this.cacheTimeout) {
129
+ return cached.template;
130
+ } else {
131
+ this.cache.delete(templateId);
132
+ }
133
+ }
134
+ return null;
135
+ }
136
+
137
+ /**
138
+ * Fetch list of available templates (optional feature)
139
+ * @param apiKey - Optional API key for authentication
140
+ * @param token - Optional Bearer token for authentication
141
+ * @returns Array of template metadata
142
+ */
143
+ async fetchTemplateList(apiKey?: string, token?: string): Promise<Array<{ id: string; name: Record<string, string>; version?: string; status?: string }>> {
144
+ try {
145
+ const url = `${this.backendServiceURL}/templates/kyc/templates`;
146
+ logger.log(`Fetching template list from: ${url}`);
147
+
148
+ const response = await axios.get<Array<{ id: string; name: Record<string, string>; version?: string; status?: string }>>(url, {
149
+ headers: this.getHeaders(apiKey, token),
150
+ timeout: 30000,
151
+ });
152
+
153
+ logger.log(`Fetched ${response.data.length} templates`);
154
+ return response.data;
155
+ } catch (error: any) {
156
+ logger.error('Error fetching template list:', JSON.stringify(errorMessage(error), null, 2));
157
+ throw new Error(`Failed to fetch template list: ${error.message || 'Unknown error'}`);
158
+ }
159
+ }
160
+ }
161
+
162
+ // Export singleton instance as default
163
+ const templateService = new TemplateServiceClass();
164
+ export default templateService;
165
+
166
+ // Also export the class for advanced usage
167
+ // export { TemplateServiceClass };
@@ -35,6 +35,7 @@ export interface VerificationResult {
35
35
  personal_information: {
36
36
  full_name: string;
37
37
  first_name: string | null;
38
+ given_name: string | null;
38
39
  last_name: string | null;
39
40
  date_of_birth: string;
40
41
  sex: string;
@@ -325,6 +325,12 @@ export type GovernmentDocumentType =
325
325
  | 'permanent_residence'
326
326
  | 'national_id'
327
327
  | 'work_permit'
328
+ | 'id'
329
+ | 'pp'
330
+ | 'dl'
331
+ | 'hic'
332
+ | 'pr'
333
+ | 'wp'
328
334
 
329
335
  export const GovernmentDocumentTypeShorted: Record<GovernmentDocumentType, string> = {
330
336
  identity_card: 'id',
@@ -334,6 +340,22 @@ export const GovernmentDocumentTypeShorted: Record<GovernmentDocumentType, strin
334
340
  permanent_residence: 'pr',
335
341
  national_id: 'id',
336
342
  work_permit: 'wp',
343
+ id: 'id',
344
+ pp: 'pp',
345
+ dl: 'dl',
346
+ hic: 'hic',
347
+ pr: 'pr',
348
+ wp: 'wp',
349
+ }
350
+
351
+ export const GovernmentDocumentTypeBackend: Record<string, GovernmentDocumentType> = {
352
+ national_id: 'identity_card',
353
+ passport: 'passport',
354
+ driversLicense: 'drivers_licence',
355
+ healthInsuranceCard: 'health_insurance_card',
356
+ permanentResidence: 'permanent_residence',
357
+ workPermit: 'work_permit',
358
+ residencePermit: 'permanent_residence',
337
359
  }
338
360
 
339
361
  // Configuration spécifique pour chaque type de composant
@@ -342,7 +364,7 @@ export interface IDCardConfig {
342
364
  allowed_formats: string[];
343
365
  max_size_mb: number;
344
366
  document_types: GovernmentDocumentType[]; // Types de documents disponibles
345
- bbox_configs: Record<GovernmentDocumentType, {
367
+ bbox_configs?: Record<GovernmentDocumentType, {
346
368
  xMin: number;
347
369
  yMin: number;
348
370
  xMax: number;
@@ -383,12 +405,13 @@ export type ComponentConfig =
383
405
  | SelfieConfig
384
406
  | FileUploadConfig
385
407
  | LocationConfig
386
- | CountrySelectionConfig;
408
+ | CountrySelectionConfig
409
+ | WelcomeConfig;
387
410
 
388
411
  // Nouveau type de composant basé sur le template JSON
389
412
  export interface TemplateComponent {
390
413
  id: number;
391
- type: 'id_card' | 'selfie' | 'file_upload' | 'location' | 'country_selection' | 'initialization' | 'review_submit' | 'verification_progress';
414
+ type: 'id_card' | 'selfie' | 'file_upload' | 'location' | 'country_selection' | 'initialization' | 'review_submit' | 'verification_progress' | 'welcome';
392
415
  order: number;
393
416
  templateId?: number;
394
417
  labels: LocalizedText | Record<string, LocalizedText>;
@@ -502,4 +525,166 @@ export interface Country {
502
525
  flag: string;
503
526
  regionMapping?: ICountryMapping;
504
527
  hasRegions?: boolean;
528
+ }
529
+
530
+ // ===== TYPES FOR BACKEND TEMPLATE FORMAT =====
531
+
532
+ // Backend component configuration types
533
+ export interface WelcomeConfig {
534
+ subtitle?: string;
535
+ buttonText?: string;
536
+ requirements?: string[];
537
+ estimatedTime?: string;
538
+ consentOptions?: {
539
+ showPrivacyPolicy?: boolean;
540
+ showTermsOfService?: boolean;
541
+ showMarketingConsent?: boolean;
542
+ };
543
+ welcomeMessage?: string;
544
+ showEstimatedTime?: boolean;
545
+ }
546
+
547
+ export interface LocationCaptureConfig {
548
+ showMap?: boolean;
549
+ accuracy?: string;
550
+ required?: boolean;
551
+ maxDistance?: number;
552
+ captureMethod?: string;
553
+ allowManualEntry?: boolean;
554
+ countryRestrictions?: string[];
555
+ }
556
+
557
+ export interface GovernmentIdConfig {
558
+ required?: boolean;
559
+ allowSkip?: boolean;
560
+ cameraOverlay?: {
561
+ bbox?: {
562
+ xMax?: number;
563
+ xMin?: number;
564
+ yMax?: number;
565
+ yMin?: number;
566
+ borderColor?: string;
567
+ borderWidth?: number;
568
+ cornerRadius?: number;
569
+ };
570
+ };
571
+ documentTypes?: string[];
572
+ requiredSides?: string;
573
+ cameraSettings?: {
574
+ quality?: number;
575
+ flashMode?: string;
576
+ maxRetakes?: number;
577
+ allowRetake?: boolean;
578
+ aspectRatio?: number;
579
+ };
580
+ validationRules?: string[];
581
+ regionsByCountry?: Record<string, any>;
582
+ selectedCountries?: string[];
583
+ authenticationMethods?: Record<string, Record<string, {
584
+ back?: string[];
585
+ front?: string[];
586
+ mrzTypes?: string[];
587
+ }>>;
588
+ documentTypesByCountry?: Record<string, string[]>;
589
+ instructionsByDocumentType?: Record<string, LocalizedText>;
590
+ }
591
+
592
+ export interface SelfieCaptureConfig {
593
+ timeout?: number;
594
+ maxAttempts?: number;
595
+ faceMatching?: boolean;
596
+ captureMethod?: string;
597
+ qualityChecks?: string[];
598
+ livenessChecks?: string[];
599
+ matchThreshold?: string;
600
+ livenessEnabled?: boolean;
601
+ }
602
+
603
+ export interface ReviewSubmitConfig {
604
+ allowEditing?: boolean;
605
+ displayOptions?: string[];
606
+ requireConsent?: boolean;
607
+ postSubmitActions?: string[];
608
+ }
609
+
610
+ export type BackendComponentConfig =
611
+ | WelcomeConfig
612
+ | LocationCaptureConfig
613
+ | GovernmentIdConfig
614
+ | SelfieCaptureConfig
615
+ | ReviewSubmitConfig
616
+ | Record<string, any>;
617
+
618
+ // Backend component translation structure
619
+ export interface BackendComponentTranslations {
620
+ en?: {
621
+ title?: string;
622
+ description?: string;
623
+ instructions?: string;
624
+ buttonText?: string;
625
+ consentText?: string;
626
+ successMessage?: string;
627
+ };
628
+ fr?: {
629
+ title?: string;
630
+ description?: string;
631
+ instructions?: string;
632
+ buttonText?: string;
633
+ consentText?: string;
634
+ successMessage?: string;
635
+ };
636
+ [key: string]: {
637
+ title?: string;
638
+ description?: string;
639
+ instructions?: string;
640
+ buttonText?: string;
641
+ consentText?: string;
642
+ successMessage?: string;
643
+ } | undefined;
644
+ }
645
+
646
+ // Backend template component
647
+ export interface BackendTemplateComponent {
648
+ id: string;
649
+ type: 'welcome' | 'location-capture' | 'government-id' | 'selfie-capture' | 'review-submit' | 'verification-progress';
650
+ order: number;
651
+ config: BackendComponentConfig;
652
+ required?: boolean;
653
+ translations: BackendComponentTranslations;
654
+ }
655
+
656
+ // Backend template structure
657
+ export interface BackendKYCTemplate {
658
+ id: string;
659
+ name: {
660
+ en: string;
661
+ fr: string;
662
+ [key: string]: string;
663
+ };
664
+ status?: string;
665
+ version?: string;
666
+ components: BackendTemplateComponent[];
667
+ description?: {
668
+ en: string;
669
+ fr: string;
670
+ [key: string]: string;
671
+ };
672
+ }
673
+
674
+ // API response for template
675
+ export interface TemplateAPIResponse {
676
+ id: string;
677
+ name: {
678
+ en: string;
679
+ fr: string;
680
+ [key: string]: string;
681
+ };
682
+ status?: string;
683
+ version?: string;
684
+ components: BackendTemplateComponent[];
685
+ description?: {
686
+ en: string;
687
+ fr: string;
688
+ [key: string]: string;
689
+ };
505
690
  }
@@ -1,5 +1,7 @@
1
1
  import { Image as RNImage, Platform } from 'react-native';
2
2
  import * as ImageManipulator from "expo-image-manipulator";
3
+ import { truncateFields } from '../modules/api/KYCService';
4
+ import { logger } from './logger';
3
5
 
4
6
  type Point = [number, number];
5
7
 
@@ -81,13 +83,14 @@ export async function cropImageWithBBox(uri: string, bbox: any) {
81
83
 
82
84
  // 3️⃣ Convertir le bbox à la taille réelle scale 0.10 = 10%
83
85
  const crop = {
84
- originX: bbox.minX ,
85
- originY: bbox.minY ,
86
- width: bbox.width ,
86
+ originX: bbox.minX,
87
+ originY: bbox.minY,
88
+ width: bbox.width,
87
89
  height: bbox.height,
88
90
  };
89
91
 
90
92
  // 4️⃣ Appliquer le crop
93
+ // @ts-ignore - manipulateAsync is deprecated but still functional, new API (useImageManipulator) is a React hook and not suitable for utility functions
91
94
  const result = await ImageManipulator.manipulateAsync(
92
95
  uri,
93
96
  [{ crop }],
@@ -97,3 +100,80 @@ export async function cropImageWithBBox(uri: string, bbox: any) {
97
100
  console.log("Image recadrée:", result.uri);
98
101
  return result.uri;
99
102
  }
103
+
104
+ // Fonction pour rogner avec une tolérance de 10% autour du bbox
105
+ export async function cropImageWithBBoxWithTolerance(uri: string, bbox: any, tolerance: number = 0.1): Promise<string> {
106
+ console.log("cropImageWithBBoxWithTolerance", JSON.stringify(truncateFields({ uri, bbox, tolerance }), null, 2));
107
+
108
+ return new Promise<string>((resolve, reject) => {
109
+ let isResolved = false;
110
+
111
+ // Timeout de sécurité (15 secondes)
112
+ const timeout = setTimeout(() => {
113
+ if (!isResolved) {
114
+ isResolved = true;
115
+ console.warn("Timeout lors du rognage avec tolérance, utilisation de l'image originale");
116
+ resolve(uri); // Retourner l'URI original en cas de timeout
117
+ }
118
+ }, 15000);
119
+
120
+ // 1️⃣ Récupère la taille originale de l'image
121
+ RNImage.getSize(uri, (imageWidth, imageHeight) => {
122
+ if (isResolved) return;
123
+
124
+ // 2️⃣ Calculer la tolérance en pixels (10% de chaque côté)
125
+ const toleranceX = bbox.width * tolerance;
126
+ const toleranceY = bbox.height * tolerance;
127
+
128
+ // 3️⃣ Ajuster le bbox avec la tolérance
129
+ // Calculer les nouvelles coordonnées avec tolérance
130
+ const newMinX = Math.max(0, bbox.minX - toleranceX);
131
+ const newMinY = Math.max(0, bbox.minY - toleranceY);
132
+ const newMaxX = Math.min(imageWidth, bbox.minX + bbox.width + toleranceX);
133
+ const newMaxY = Math.min(imageHeight, bbox.minY + bbox.height + toleranceY);
134
+
135
+ // Calculer la largeur et hauteur finales
136
+ const finalWidth = newMaxX - newMinX;
137
+ const finalHeight = newMaxY - newMinY;
138
+
139
+ const crop = {
140
+ originX: newMinX,
141
+ originY: newMinY,
142
+ width: finalWidth,
143
+ height: finalHeight,
144
+ };
145
+
146
+ logger.log("cropImageWithBBoxWithTolerance - crop params", JSON.stringify(truncateFields({ uri, crop, tolerance, imageWidth, imageHeight }), null, 2));
147
+
148
+ // 4️⃣ Appliquer le crop avec tolérance
149
+ // @ts-ignore - manipulateAsync is deprecated but still functional, new API (useImageManipulator) is a React hook and not suitable for utility functions
150
+ ImageManipulator.manipulateAsync(
151
+ uri,
152
+ [{ crop }],
153
+ { compress: 0.95, format: ImageManipulator.SaveFormat.JPEG }
154
+ )
155
+ .then((result) => {
156
+ if (isResolved) return;
157
+ isResolved = true;
158
+ clearTimeout(timeout);
159
+ console.log("Image recadrée avec tolérance:", result.uri);
160
+ resolve(result.uri);
161
+ })
162
+ .catch((error) => {
163
+ if (isResolved) return;
164
+ isResolved = true;
165
+ clearTimeout(timeout);
166
+ console.error("Erreur lors du rognage avec tolérance:", error);
167
+ // En cas d'erreur, retourner l'URI original au lieu de rejeter
168
+ resolve(uri);
169
+ });
170
+ }, (error) => {
171
+ if (isResolved) return;
172
+ isResolved = true;
173
+ clearTimeout(timeout);
174
+ console.error("Erreur lors de la récupération de la taille de l'image:", error);
175
+ // En cas d'erreur, retourner l'URI original au lieu de rejeter
176
+ resolve(uri);
177
+ });
178
+ });
179
+ }
@@ -0,0 +1,85 @@
1
+ import { Platform, Alert } from 'react-native';
2
+
3
+ export interface AlertButton {
4
+ text?: string;
5
+ onPress?: () => void;
6
+ style?: 'default' | 'cancel' | 'destructive';
7
+ }
8
+
9
+ /**
10
+ * Platform-aware alert helper that works on both native and web platforms
11
+ * On web, uses window.confirm/window.alert since Alert.alert is not supported
12
+ * On native, uses the standard Alert.alert
13
+ */
14
+ export const showAlert = (
15
+ title: string,
16
+ message?: string,
17
+ buttons?: AlertButton[]
18
+ ): void => {
19
+ if (Platform.OS === 'web') {
20
+ // Web implementation using native browser dialogs
21
+ const fullMessage = message ? `${title}\n\n${message}` : title;
22
+
23
+ if (buttons && buttons.length > 1) {
24
+ // Multiple buttons - use confirm dialog
25
+ const confirmed = window.confirm(fullMessage);
26
+
27
+ if (confirmed) {
28
+ // Find and call the primary/positive button callback
29
+ const confirmButton = buttons.find(
30
+ btn => btn.style !== 'cancel' && btn.onPress
31
+ );
32
+ confirmButton?.onPress?.();
33
+ } else {
34
+ // Find and call the cancel button callback
35
+ const cancelButton = buttons.find(btn => btn.style === 'cancel');
36
+ cancelButton?.onPress?.();
37
+ }
38
+ } else if (buttons && buttons.length === 1) {
39
+ // Single button - use alert dialog
40
+ window.alert(fullMessage);
41
+ buttons[0]?.onPress?.();
42
+ } else {
43
+ // No buttons - just show alert
44
+ window.alert(fullMessage);
45
+ }
46
+ } else {
47
+ // Native implementation - use standard Alert.alert
48
+ Alert.alert(title, message, buttons);
49
+ }
50
+ };
51
+
52
+ /**
53
+ * Simplified confirm dialog for yes/no scenarios
54
+ */
55
+ export const showConfirm = (
56
+ title: string,
57
+ message: string,
58
+ onConfirm: () => void,
59
+ onCancel?: () => void
60
+ ): void => {
61
+ showAlert(title, message, [
62
+ {
63
+ text: 'Cancel',
64
+ style: 'cancel',
65
+ onPress: onCancel,
66
+ },
67
+ {
68
+ text: 'OK',
69
+ onPress: onConfirm,
70
+ },
71
+ ]);
72
+ };
73
+
74
+ /**
75
+ * Simple alert with just an OK button
76
+ */
77
+ export const showSimpleAlert = (title: string, message?: string, onPress?: () => void): void => {
78
+ showAlert(title, message, [
79
+ {
80
+ text: 'OK',
81
+ onPress,
82
+ },
83
+ ]);
84
+ };
85
+