datakeen-session-react 1.1.193 → 1.1.194-rc.1

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 (64) hide show
  1. package/.gitlab-ci.yml +9 -3
  2. package/dist/cjs/components/document-collection/DocumentProcessing.js +18 -2
  3. package/dist/cjs/components/document-collection/DocumentProcessing.js.map +1 -1
  4. package/dist/cjs/components/id-check/BeforePhoto.js +1 -1
  5. package/dist/cjs/components/id-check/BeforePhoto.js.map +1 -1
  6. package/dist/cjs/components/id-check/CameraMask.js +12 -6
  7. package/dist/cjs/components/id-check/CameraMask.js.map +1 -1
  8. package/dist/cjs/components/id-check/Photo.js +66 -18
  9. package/dist/cjs/components/id-check/Photo.js.map +1 -1
  10. package/dist/cjs/components/jdi/JDIProcessing.js +17 -2
  11. package/dist/cjs/components/jdi/JDIProcessing.js.map +1 -1
  12. package/dist/cjs/components/start-flow/CGU.js +1 -1
  13. package/dist/cjs/components/start-flow/Start.js +1 -1
  14. package/dist/cjs/i18n/en.json.js +16 -14
  15. package/dist/cjs/i18n/en.json.js.map +1 -1
  16. package/dist/cjs/i18n/es.json.js +1208 -0
  17. package/dist/cjs/i18n/es.json.js.map +1 -0
  18. package/dist/cjs/i18n/fr.json.js +8 -6
  19. package/dist/cjs/i18n/fr.json.js.map +1 -1
  20. package/dist/cjs/i18n/index.js +5 -1
  21. package/dist/cjs/i18n/index.js.map +1 -1
  22. package/dist/cjs/index.css.js +1 -1
  23. package/dist/cjs/services/cameraService.js +34 -90
  24. package/dist/cjs/services/cameraService.js.map +1 -1
  25. package/dist/cjs/services/documentEdgeDetectionService.js +182 -0
  26. package/dist/cjs/services/documentEdgeDetectionService.js.map +1 -0
  27. package/dist/cjs/utils/analysisErrors.js +18 -0
  28. package/dist/cjs/utils/analysisErrors.js.map +1 -1
  29. package/dist/cjs/utils/languageFlags.js +6 -0
  30. package/dist/cjs/utils/languageFlags.js.map +1 -1
  31. package/dist/esm/components/document-collection/DocumentProcessing.js +19 -3
  32. package/dist/esm/components/document-collection/DocumentProcessing.js.map +1 -1
  33. package/dist/esm/components/id-check/BeforePhoto.js +1 -1
  34. package/dist/esm/components/id-check/BeforePhoto.js.map +1 -1
  35. package/dist/esm/components/id-check/CameraMask.js +12 -6
  36. package/dist/esm/components/id-check/CameraMask.js.map +1 -1
  37. package/dist/esm/components/id-check/Photo.js +66 -18
  38. package/dist/esm/components/id-check/Photo.js.map +1 -1
  39. package/dist/esm/components/jdi/JDIProcessing.js +18 -3
  40. package/dist/esm/components/jdi/JDIProcessing.js.map +1 -1
  41. package/dist/esm/components/start-flow/CGU.js +1 -1
  42. package/dist/esm/components/start-flow/Start.js +1 -1
  43. package/dist/esm/i18n/en.json.js +16 -14
  44. package/dist/esm/i18n/en.json.js.map +1 -1
  45. package/dist/esm/i18n/es.json.js +1167 -0
  46. package/dist/esm/i18n/es.json.js.map +1 -0
  47. package/dist/esm/i18n/fr.json.js +8 -6
  48. package/dist/esm/i18n/fr.json.js.map +1 -1
  49. package/dist/esm/i18n/index.js +5 -1
  50. package/dist/esm/i18n/index.js.map +1 -1
  51. package/dist/esm/index.css.js +1 -1
  52. package/dist/esm/services/cameraService.js +34 -90
  53. package/dist/esm/services/cameraService.js.map +1 -1
  54. package/dist/esm/services/documentEdgeDetectionService.js +176 -0
  55. package/dist/esm/services/documentEdgeDetectionService.js.map +1 -0
  56. package/dist/esm/utils/analysisErrors.js +17 -1
  57. package/dist/esm/utils/analysisErrors.js.map +1 -1
  58. package/dist/esm/utils/languageFlags.js +6 -0
  59. package/dist/esm/utils/languageFlags.js.map +1 -1
  60. package/package.json +1 -4
  61. package/dist/cjs/services/documentDetectionService.js +0 -156
  62. package/dist/cjs/services/documentDetectionService.js.map +0 -1
  63. package/dist/esm/services/documentDetectionService.js +0 -153
  64. package/dist/esm/services/documentDetectionService.js.map +0 -1
package/.gitlab-ci.yml CHANGED
@@ -132,9 +132,11 @@ publish:main:
132
132
  - git config user.email "ci@datakeen.io"
133
133
  - git config user.name "Datakeen CI"
134
134
  - git remote set-url origin https://${CI_PUSH_USER}:${CI_PUSH_TOKEN}@gitlab.datakeen.co/datakeenteam/teamfullstack/client-sessions-react-sdk.git
135
+ - git fetch origin "$CI_COMMIT_BRANCH"
136
+ - git checkout -B "$CI_COMMIT_BRANCH" "origin/$CI_COMMIT_BRANCH"
135
137
  - 'npm version patch -m "chore(release): %s"'
136
138
  - export TARGET_TAG="v$(node -p "require('./package.json').version")"
137
- - git push -o ci.skip origin HEAD:main
139
+ - git push -o ci.skip origin "$CI_COMMIT_BRANCH"
138
140
  - git push -o ci.skip origin --tags
139
141
  - git remote add relay "https://oauth2:${RELAY_API_TOKEN}@gitlab.com/datakeen-ci-relay/npm-publish-relay.git" 2>/dev/null || git remote set-url relay "https://oauth2:${RELAY_API_TOKEN}@gitlab.com/datakeen-ci-relay/npm-publish-relay.git"
140
142
  - git checkout --orphan "relay-snapshot-${CI_JOB_ID}"
@@ -185,9 +187,11 @@ publish:staging:
185
187
  - git config user.email "ci@datakeen.io"
186
188
  - git config user.name "Datakeen CI"
187
189
  - git remote set-url origin https://${CI_PUSH_USER}:${CI_PUSH_TOKEN}@gitlab.datakeen.co/datakeenteam/teamfullstack/client-sessions-react-sdk.git
190
+ - git fetch origin "$CI_COMMIT_BRANCH"
191
+ - git checkout -B "$CI_COMMIT_BRANCH" "origin/$CI_COMMIT_BRANCH"
188
192
  - 'npm version prerelease --preid=rc -m "chore(release): %s"'
189
193
  - export TARGET_TAG="v$(node -p "require('./package.json').version")"
190
- - git push -o ci.skip origin HEAD:staging
194
+ - git push -o ci.skip origin "$CI_COMMIT_BRANCH"
191
195
  - git push -o ci.skip origin --tags
192
196
  - git remote add relay "https://oauth2:${RELAY_API_TOKEN}@gitlab.com/datakeen-ci-relay/npm-publish-relay.git" 2>/dev/null || git remote set-url relay "https://oauth2:${RELAY_API_TOKEN}@gitlab.com/datakeen-ci-relay/npm-publish-relay.git"
193
197
  - git checkout --orphan "relay-snapshot-${CI_JOB_ID}"
@@ -238,9 +242,11 @@ publish:dev:
238
242
  - git config user.email "ci@datakeen.io"
239
243
  - git config user.name "Datakeen CI"
240
244
  - git remote set-url origin https://${CI_PUSH_USER}:${CI_PUSH_TOKEN}@gitlab.datakeen.co/datakeenteam/teamfullstack/client-sessions-react-sdk.git
245
+ - git fetch origin "$CI_COMMIT_BRANCH"
246
+ - git checkout -B "$CI_COMMIT_BRANCH" "origin/$CI_COMMIT_BRANCH"
241
247
  - 'npm version prerelease --preid=dev -m "chore(release): %s"'
242
248
  - export TARGET_TAG="v$(node -p "require('./package.json').version")"
243
- - git push -o ci.skip origin HEAD:dev
249
+ - git push -o ci.skip origin "$CI_COMMIT_BRANCH"
244
250
  - git push -o ci.skip origin --tags
245
251
  - git remote add relay "https://oauth2:${RELAY_API_TOKEN}@gitlab.com/datakeen-ci-relay/npm-publish-relay.git" 2>/dev/null || git remote set-url relay "https://oauth2:${RELAY_API_TOKEN}@gitlab.com/datakeen-ci-relay/npm-publish-relay.git"
246
252
  - git checkout --orphan "relay-snapshot-${CI_JOB_ID}"
@@ -135,7 +135,7 @@ var DocumentProcessing = React.memo(function (_a) {
135
135
  return;
136
136
  }
137
137
  var processFiles = function () { return tslib_es6.__awaiter(void 0, void 0, void 0, function () {
138
- var _loop_1, i, error_1, isUploadNotConfirmed;
138
+ var _loop_1, i, error_1, isUploadNotConfirmed, isQuotaExceeded, apiError;
139
139
  return tslib_es6.__generator(this, function (_a) {
140
140
  switch (_a.label) {
141
141
  case 0:
@@ -213,10 +213,26 @@ var DocumentProcessing = React.memo(function (_a) {
213
213
  });
214
214
  });
215
215
  isUploadNotConfirmed = analysisErrors.isS3UploadNotConfirmedError(error_1);
216
+ isQuotaExceeded = analysisErrors.isQuotaExceededError(error_1);
217
+ apiError = error_1;
216
218
  setAnalysisResult({
217
219
  success: false,
218
220
  data: null,
219
- canRetry: isUploadNotConfirmed,
221
+ // Propage le code/message QUOTA_EXCEEDED pour que DocumentError
222
+ // puisse afficher un message dédié via `user_guidance.<code>` au
223
+ // lieu de rester muet sur la cause (limité au cas quota : les
224
+ // autres erreurs gardent leur comportement existant).
225
+ predictions: isQuotaExceeded
226
+ ? [
227
+ {
228
+ code: analysisErrors.QUOTA_EXCEEDED_CODE,
229
+ codeName: analysisErrors.QUOTA_EXCEEDED_CODE,
230
+ codeDescription: (apiError === null || apiError === void 0 ? void 0 : apiError.message) || "",
231
+ type: "document",
232
+ },
233
+ ]
234
+ : undefined,
235
+ canRetry: isUploadNotConfirmed || isQuotaExceeded,
220
236
  blockContinue: isUploadNotConfirmed,
221
237
  });
222
238
  setIsComplete(true);
@@ -1 +1 @@
1
- {"version":3,"file":"DocumentProcessing.js","sources":["../../../../../src/components/document-collection/DocumentProcessing.tsx"],"sourcesContent":["import { useEffect, useRef, useState, useMemo, useCallback, memo } from \"react\";\nimport Title from \"../ui/Title\";\nimport Subtitle from \"../ui/Subtitle\";\nimport { useI18n } from \"../../hooks/useI18n\";\nimport {\n analyzeFiles,\n type AnalyzeFilesOptions,\n} from \"../../services/analysis\";\nimport { getActiveSessionId } from \"../../services/sessionMemoryStore\";\nimport type { onUploadFiles } from \"../../types/uploadFiles\";\nimport type {\n DocumentProcessingProps,\n ProcessingStep,\n ProcessingResult,\n} from \"./types\";\nimport type { Prediction } from \"../../utils/apiAnalysis\";\nimport { isS3UploadNotConfirmedError } from \"../../utils/analysisErrors\";\n\n// Localized processing steps generator\nconst getProcessingSteps = (t: any): ProcessingStep[] => [\n {\n title: t(\n \"processing.steps.document_reception.title\",\n \"Réception du document\",\n ),\n subtitle: t(\n \"processing.steps.document_reception.subtitle\",\n \"Téléchargement et vérification du format\",\n ),\n hasError: false,\n },\n {\n title: t(\n \"processing.steps.quality_analysis.title\",\n \"Analyse de la qualité\",\n ),\n subtitle: t(\n \"processing.steps.quality_analysis.subtitle\",\n \"Contrôle de la netteté et de l'éclairage\",\n ),\n hasError: false,\n },\n {\n title: t(\n \"processing.steps.data_extraction.title\",\n \"Extraction des données\",\n ),\n subtitle: t(\n \"processing.steps.data_extraction.subtitle\",\n \"Lecture des informations du document\",\n ),\n hasError: false,\n },\n {\n title: t(\n \"processing.steps.final_verification.title\",\n \"Vérification finale\",\n ),\n subtitle: t(\n \"processing.steps.final_verification.subtitle\",\n \"Validation des données extraites\",\n ),\n hasError: false,\n },\n];\n\nconst DocumentProcessing = memo(\n ({\n onProcessingComplete,\n documentType,\n documentTypeLabel,\n nodeId,\n fileUploaded,\n documentTypeId,\n retryCount = 0,\n config,\n customAnalysisFunction,\n }: DocumentProcessingProps) => {\n const inferRequiresTwoSides = useCallback(\n (type?: string): boolean | undefined => {\n if (!type) {\n return undefined;\n }\n\n const normalized = type.trim().toLowerCase();\n if (!normalized) {\n return undefined;\n }\n\n if (normalized.includes(\"passport\")) {\n return false;\n }\n\n if (\n normalized.includes(\"idcard\") ||\n normalized.includes(\"identity\") ||\n normalized.includes(\"driver\") ||\n normalized.includes(\"licenc\") ||\n normalized.includes(\"residence\")\n ) {\n return true;\n }\n\n return undefined;\n },\n [],\n );\n\n const { t, translateDocumentType } = useI18n();\n\n const [isProcessing, setIsProcessing] = useState(false);\n const [currentStep, setCurrentStep] = useState(0);\n const [processingSteps, setProcessingSteps] = useState<ProcessingStep[]>(\n () => config?.steps || getProcessingSteps(t),\n );\n const [isComplete, setIsComplete] = useState(false);\n const [analysisResult, setAnalysisResult] =\n useState<ProcessingResult | null>(null);\n\n const processingStartedRef = useRef(false);\n const processedFilesRef = useRef<string | null>(null);\n\n // Create stable key for uploaded files\n const filesKey = useMemo(() => {\n if (!fileUploaded) return null;\n\n // Handle onUploadFiles structure\n return `${fileUploaded.front?.substring(0, 50) || \"no-front\"}-${\n fileUploaded.back?.substring(0, 50) || \"no-back\"\n }`;\n }, [fileUploaded]);\n\n // Stable callback for completion\n const stableOnProcessingComplete = useCallback(\n (\n success: boolean,\n retryCount?: number,\n analysisData?: Prediction[] | null,\n blockContinue?: boolean,\n ) => {\n console.log(\"[DocumentProcessing] Stable callback for completion:\", success, retryCount);\n if (blockContinue === true) {\n onProcessingComplete(success, retryCount, analysisData, true);\n } else {\n onProcessingComplete(success, retryCount, analysisData);\n }\n },\n [onProcessingComplete],\n );\n\n // Default analysis function\n const defaultAnalysisFunction = useCallback(\n async (\n sessionId: string,\n files: onUploadFiles,\n docTypeId: string,\n options?: AnalyzeFilesOptions,\n ): Promise<ProcessingResult> => {\n const response = await analyzeFiles(\n sessionId,\n nodeId,\n files,\n docTypeId,\n {\n ...options,\n save: options?.save ?? false, // don't save to session yet\n incrementAnalysis: options?.incrementAnalysis ?? false, // don't auto-progress session\n },\n );\n\n // When polling is enabled, response is AnalysisStatusResponse\n // which has code, codeName, codeDescription directly on the object\n const extractedCode = response?.code || String(response?.status);\n const codeName = response?.codeName;\n const codeDescription = response?.codeDescription;\n\n // Construct predictions array from the response\n const predictions = [];\n if (extractedCode && codeName) {\n predictions.push({\n code: extractedCode,\n codeName: codeName,\n codeDescription: codeDescription || \"\",\n type: \"document\", // default type\n });\n }\n\n // Determine success based on code\n // Code 8.x (specimen) is blocking — treated as failure\n const isSuccess = [\"1.\", \"4.\", \"5.\", \"6.\", \"9.\"].some((prefix) =>\n extractedCode.startsWith(prefix),\n );\n\n return {\n success: isSuccess,\n code: extractedCode,\n data: response,\n predictions: predictions,\n };\n },\n [nodeId],\n );\n\n // Main processing effect\n useEffect(() => {\n // Prevent multiple calls\n if (\n !fileUploaded ||\n !filesKey ||\n isProcessing ||\n processingStartedRef.current\n ) {\n return;\n }\n\n const sessionId = getActiveSessionId();\n if (!sessionId) {\n stableOnProcessingComplete(false);\n return;\n }\n\n // Check if we already processed these same files\n if (processedFilesRef.current === filesKey) {\n return;\n }\n\n const processFiles = async () => {\n try {\n // Mark processing start\n setIsProcessing(true);\n processingStartedRef.current = true;\n processedFilesRef.current = filesKey;\n\n // Animate through steps with real API call at the end\n for (let i = 0; i < processingSteps.length; i++) {\n setCurrentStep(i);\n\n if (i < processingSteps.length - 1) {\n // Delay between steps (except last one)\n await new Promise((resolve) => setTimeout(resolve, 800));\n } else {\n // Last step: real API call\n const analysisFunction =\n customAnalysisFunction || defaultAnalysisFunction;\n const inferredTwoSides = inferRequiresTwoSides(\n typeof config?.documentType === \"string\"\n ? config.documentType\n : documentType,\n );\n\n const analysisOptions: AnalyzeFilesOptions = {\n documentTypeKey: documentType,\n requiresTwoSides:\n typeof inferredTwoSides === \"boolean\"\n ? inferredTwoSides\n : Boolean(fileUploaded.back),\n };\n\n const result = await analysisFunction(\n sessionId,\n fileUploaded,\n documentTypeId,\n analysisOptions,\n );\n\n setAnalysisResult(result);\n\n // Mark current step according to result\n setProcessingSteps((prev) =>\n prev.map((step, index) =>\n index === i ? { ...step, hasError: !result.success } : step,\n ),\n );\n\n // Small delay to see result\n await new Promise((resolve) => setTimeout(resolve, 500));\n }\n }\n\n setCurrentStep(processingSteps.length);\n setIsComplete(true);\n } catch (error) {\n console.error(\"Processing error:\", error);\n\n // Mark current step as error\n setProcessingSteps((prev) =>\n prev.map((step, index) =>\n index === currentStep ? { ...step, hasError: true } : step,\n ),\n );\n\n const isUploadNotConfirmed = isS3UploadNotConfirmedError(error);\n setAnalysisResult({\n success: false,\n data: null,\n canRetry: isUploadNotConfirmed,\n blockContinue: isUploadNotConfirmed,\n });\n setIsComplete(true);\n } finally {\n setIsProcessing(false);\n }\n };\n\n processFiles();\n }, [\n fileUploaded,\n filesKey,\n documentTypeId,\n documentType,\n config?.documentType,\n isProcessing,\n stableOnProcessingComplete,\n processingSteps.length,\n currentStep,\n customAnalysisFunction,\n defaultAnalysisFunction,\n inferRequiresTwoSides,\n ]);\n\n // Auto-complete when analysis is done\n useEffect(() => {\n if (isComplete && analysisResult) {\n setTimeout(() => {\n stableOnProcessingComplete(\n analysisResult.success,\n retryCount,\n analysisResult.predictions || null,\n analysisResult.blockContinue,\n );\n }, 1000);\n }\n }, [isComplete, analysisResult, stableOnProcessingComplete, retryCount]);\n\n return (\n <div className=\"flex flex-col justify-between h-full w-full\">\n {/* Main content area */}\n <div className=\"flex-1 px-4 py-6 pt-11 md:px-8 md:py-8\">\n <div className=\"w-full max-w-md mx-auto space-y-6\">\n {/* Header */}\n <div className=\"text-center space-y-4\">\n <Title className=\"text-xl md:text-2xl lg:text-3xl\">\n {config?.processingTitle ||\n t(\"processing.title\", \"Analyse en cours\")}\n </Title>\n <Subtitle className=\"text-sm text-gray-600 leading-relaxed\">\n {config?.processingSubtitle ||\n t(\"processing.subtitle\", {\n documentType: t(\n \"documentTypes.\" + documentType,\n \"document\",\n ),\n defaultValue: `Nous analysons votre ${t(\"documentTypes.\" + documentType, \"document\")}. Cela peut prendre quelques instants.`,\n })}\n </Subtitle>\n </div>\n\n {/* Processing steps with status indicators */}\n <div className=\"w-full flex justify-center\">\n <div className=\"space-y-5\">\n {processingSteps.map((step, index: number) => (\n <div key={index} className=\"flex items-start\">\n {/* Status indicator */}\n <div className=\"mr-4 mt-1 flex-shrink-0\">\n {index < currentStep ? (\n step.hasError ? (\n // Error indicator - Red X\n <div className=\"flex items-center justify-center w-6 h-6 rounded-full bg-red-500 text-white text-xs\">\n ✕\n </div>\n ) : (\n // Success indicator - Green checkmark\n <div className=\"flex items-center justify-center w-6 h-6 rounded-full bg-[#11E5C5] text-white text-xs\">\n ✓\n </div>\n )\n ) : index === currentStep ? (\n // Loading spinner only for the current step\n <div className=\"w-6 h-6 rounded-full border-2 border-t-[#11E5C5] border-r-[#11E5C5] border-b-[#11E5C5] border-l-transparent animate-spin\"></div>\n ) : (\n // Empty circle for future steps\n <div className=\"w-6 h-6 rounded-full border-2 border-gray-300\"></div>\n )}\n </div>\n\n {/* Step content */}\n <div className=\"flex-1 min-w-0\">\n <p className=\"font-medium text-[#3C3C40] text-sm\">\n {step.title}\n </p>\n <p className=\"text-xs text-gray-500 mt-1\">\n {step.subtitle}\n </p>\n </div>\n </div>\n ))}\n </div>\n </div>\n </div>\n </div>\n </div>\n );\n },\n);\n\nDocumentProcessing.displayName = \"DocumentProcessing\";\n\nexport default DocumentProcessing;\nexport type { DocumentProcessingProps };\n"],"names":["memo","useCallback","useI18n","useState","useRef","useMemo","__awaiter","analyzeFiles","__assign","useEffect","getActiveSessionId","isS3UploadNotConfirmedError","_jsx","_jsxs","Title","Subtitle"],"mappings":"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"}
1
+ {"version":3,"file":"DocumentProcessing.js","sources":["../../../../../src/components/document-collection/DocumentProcessing.tsx"],"sourcesContent":["import { useEffect, useRef, useState, useMemo, useCallback, memo } from \"react\";\nimport Title from \"../ui/Title\";\nimport Subtitle from \"../ui/Subtitle\";\nimport { useI18n } from \"../../hooks/useI18n\";\nimport {\n analyzeFiles,\n type AnalyzeFilesOptions,\n} from \"../../services/analysis\";\nimport { getActiveSessionId } from \"../../services/sessionMemoryStore\";\nimport type { onUploadFiles } from \"../../types/uploadFiles\";\nimport type {\n DocumentProcessingProps,\n ProcessingStep,\n ProcessingResult,\n} from \"./types\";\nimport type { Prediction } from \"../../utils/apiAnalysis\";\nimport {\n isS3UploadNotConfirmedError,\n isQuotaExceededError,\n QUOTA_EXCEEDED_CODE,\n} from \"../../utils/analysisErrors\";\n\n// Localized processing steps generator\nconst getProcessingSteps = (t: any): ProcessingStep[] => [\n {\n title: t(\n \"processing.steps.document_reception.title\",\n \"Réception du document\",\n ),\n subtitle: t(\n \"processing.steps.document_reception.subtitle\",\n \"Téléchargement et vérification du format\",\n ),\n hasError: false,\n },\n {\n title: t(\n \"processing.steps.quality_analysis.title\",\n \"Analyse de la qualité\",\n ),\n subtitle: t(\n \"processing.steps.quality_analysis.subtitle\",\n \"Contrôle de la netteté et de l'éclairage\",\n ),\n hasError: false,\n },\n {\n title: t(\n \"processing.steps.data_extraction.title\",\n \"Extraction des données\",\n ),\n subtitle: t(\n \"processing.steps.data_extraction.subtitle\",\n \"Lecture des informations du document\",\n ),\n hasError: false,\n },\n {\n title: t(\n \"processing.steps.final_verification.title\",\n \"Vérification finale\",\n ),\n subtitle: t(\n \"processing.steps.final_verification.subtitle\",\n \"Validation des données extraites\",\n ),\n hasError: false,\n },\n];\n\nconst DocumentProcessing = memo(\n ({\n onProcessingComplete,\n documentType,\n documentTypeLabel,\n nodeId,\n fileUploaded,\n documentTypeId,\n retryCount = 0,\n config,\n customAnalysisFunction,\n }: DocumentProcessingProps) => {\n const inferRequiresTwoSides = useCallback(\n (type?: string): boolean | undefined => {\n if (!type) {\n return undefined;\n }\n\n const normalized = type.trim().toLowerCase();\n if (!normalized) {\n return undefined;\n }\n\n if (normalized.includes(\"passport\")) {\n return false;\n }\n\n if (\n normalized.includes(\"idcard\") ||\n normalized.includes(\"identity\") ||\n normalized.includes(\"driver\") ||\n normalized.includes(\"licenc\") ||\n normalized.includes(\"residence\")\n ) {\n return true;\n }\n\n return undefined;\n },\n [],\n );\n\n const { t, translateDocumentType } = useI18n();\n\n const [isProcessing, setIsProcessing] = useState(false);\n const [currentStep, setCurrentStep] = useState(0);\n const [processingSteps, setProcessingSteps] = useState<ProcessingStep[]>(\n () => config?.steps || getProcessingSteps(t),\n );\n const [isComplete, setIsComplete] = useState(false);\n const [analysisResult, setAnalysisResult] =\n useState<ProcessingResult | null>(null);\n\n const processingStartedRef = useRef(false);\n const processedFilesRef = useRef<string | null>(null);\n\n // Create stable key for uploaded files\n const filesKey = useMemo(() => {\n if (!fileUploaded) return null;\n\n // Handle onUploadFiles structure\n return `${fileUploaded.front?.substring(0, 50) || \"no-front\"}-${\n fileUploaded.back?.substring(0, 50) || \"no-back\"\n }`;\n }, [fileUploaded]);\n\n // Stable callback for completion\n const stableOnProcessingComplete = useCallback(\n (\n success: boolean,\n retryCount?: number,\n analysisData?: Prediction[] | null,\n blockContinue?: boolean,\n ) => {\n console.log(\"[DocumentProcessing] Stable callback for completion:\", success, retryCount);\n if (blockContinue === true) {\n onProcessingComplete(success, retryCount, analysisData, true);\n } else {\n onProcessingComplete(success, retryCount, analysisData);\n }\n },\n [onProcessingComplete],\n );\n\n // Default analysis function\n const defaultAnalysisFunction = useCallback(\n async (\n sessionId: string,\n files: onUploadFiles,\n docTypeId: string,\n options?: AnalyzeFilesOptions,\n ): Promise<ProcessingResult> => {\n const response = await analyzeFiles(\n sessionId,\n nodeId,\n files,\n docTypeId,\n {\n ...options,\n save: options?.save ?? false, // don't save to session yet\n incrementAnalysis: options?.incrementAnalysis ?? false, // don't auto-progress session\n },\n );\n\n // When polling is enabled, response is AnalysisStatusResponse\n // which has code, codeName, codeDescription directly on the object\n const extractedCode = response?.code || String(response?.status);\n const codeName = response?.codeName;\n const codeDescription = response?.codeDescription;\n\n // Construct predictions array from the response\n const predictions = [];\n if (extractedCode && codeName) {\n predictions.push({\n code: extractedCode,\n codeName: codeName,\n codeDescription: codeDescription || \"\",\n type: \"document\", // default type\n });\n }\n\n // Determine success based on code\n // Code 8.x (specimen) is blocking — treated as failure\n const isSuccess = [\"1.\", \"4.\", \"5.\", \"6.\", \"9.\"].some((prefix) =>\n extractedCode.startsWith(prefix),\n );\n\n return {\n success: isSuccess,\n code: extractedCode,\n data: response,\n predictions: predictions,\n };\n },\n [nodeId],\n );\n\n // Main processing effect\n useEffect(() => {\n // Prevent multiple calls\n if (\n !fileUploaded ||\n !filesKey ||\n isProcessing ||\n processingStartedRef.current\n ) {\n return;\n }\n\n const sessionId = getActiveSessionId();\n if (!sessionId) {\n stableOnProcessingComplete(false);\n return;\n }\n\n // Check if we already processed these same files\n if (processedFilesRef.current === filesKey) {\n return;\n }\n\n const processFiles = async () => {\n try {\n // Mark processing start\n setIsProcessing(true);\n processingStartedRef.current = true;\n processedFilesRef.current = filesKey;\n\n // Animate through steps with real API call at the end\n for (let i = 0; i < processingSteps.length; i++) {\n setCurrentStep(i);\n\n if (i < processingSteps.length - 1) {\n // Delay between steps (except last one)\n await new Promise((resolve) => setTimeout(resolve, 800));\n } else {\n // Last step: real API call\n const analysisFunction =\n customAnalysisFunction || defaultAnalysisFunction;\n const inferredTwoSides = inferRequiresTwoSides(\n typeof config?.documentType === \"string\"\n ? config.documentType\n : documentType,\n );\n\n const analysisOptions: AnalyzeFilesOptions = {\n documentTypeKey: documentType,\n requiresTwoSides:\n typeof inferredTwoSides === \"boolean\"\n ? inferredTwoSides\n : Boolean(fileUploaded.back),\n };\n\n const result = await analysisFunction(\n sessionId,\n fileUploaded,\n documentTypeId,\n analysisOptions,\n );\n\n setAnalysisResult(result);\n\n // Mark current step according to result\n setProcessingSteps((prev) =>\n prev.map((step, index) =>\n index === i ? { ...step, hasError: !result.success } : step,\n ),\n );\n\n // Small delay to see result\n await new Promise((resolve) => setTimeout(resolve, 500));\n }\n }\n\n setCurrentStep(processingSteps.length);\n setIsComplete(true);\n } catch (error) {\n console.error(\"Processing error:\", error);\n\n // Mark current step as error\n setProcessingSteps((prev) =>\n prev.map((step, index) =>\n index === currentStep ? { ...step, hasError: true } : step,\n ),\n );\n\n const isUploadNotConfirmed = isS3UploadNotConfirmedError(error);\n const isQuotaExceeded = isQuotaExceededError(error);\n const apiError = error as { code?: string; message?: string };\n setAnalysisResult({\n success: false,\n data: null,\n // Propage le code/message QUOTA_EXCEEDED pour que DocumentError\n // puisse afficher un message dédié via `user_guidance.<code>` au\n // lieu de rester muet sur la cause (limité au cas quota : les\n // autres erreurs gardent leur comportement existant).\n predictions: isQuotaExceeded\n ? [\n {\n code: QUOTA_EXCEEDED_CODE,\n codeName: QUOTA_EXCEEDED_CODE,\n codeDescription: apiError?.message || \"\",\n type: \"document\",\n },\n ]\n : undefined,\n canRetry: isUploadNotConfirmed || isQuotaExceeded,\n blockContinue: isUploadNotConfirmed,\n });\n setIsComplete(true);\n } finally {\n setIsProcessing(false);\n }\n };\n\n processFiles();\n }, [\n fileUploaded,\n filesKey,\n documentTypeId,\n documentType,\n config?.documentType,\n isProcessing,\n stableOnProcessingComplete,\n processingSteps.length,\n currentStep,\n customAnalysisFunction,\n defaultAnalysisFunction,\n inferRequiresTwoSides,\n ]);\n\n // Auto-complete when analysis is done\n useEffect(() => {\n if (isComplete && analysisResult) {\n setTimeout(() => {\n stableOnProcessingComplete(\n analysisResult.success,\n retryCount,\n analysisResult.predictions || null,\n analysisResult.blockContinue,\n );\n }, 1000);\n }\n }, [isComplete, analysisResult, stableOnProcessingComplete, retryCount]);\n\n return (\n <div className=\"flex flex-col justify-between h-full w-full\">\n {/* Main content area */}\n <div className=\"flex-1 px-4 py-6 pt-11 md:px-8 md:py-8\">\n <div className=\"w-full max-w-md mx-auto space-y-6\">\n {/* Header */}\n <div className=\"text-center space-y-4\">\n <Title className=\"text-xl md:text-2xl lg:text-3xl\">\n {config?.processingTitle ||\n t(\"processing.title\", \"Analyse en cours\")}\n </Title>\n <Subtitle className=\"text-sm text-gray-600 leading-relaxed\">\n {config?.processingSubtitle ||\n t(\"processing.subtitle\", {\n documentType: t(\n \"documentTypes.\" + documentType,\n \"document\",\n ),\n defaultValue: `Nous analysons votre ${t(\"documentTypes.\" + documentType, \"document\")}. Cela peut prendre quelques instants.`,\n })}\n </Subtitle>\n </div>\n\n {/* Processing steps with status indicators */}\n <div className=\"w-full flex justify-center\">\n <div className=\"space-y-5\">\n {processingSteps.map((step, index: number) => (\n <div key={index} className=\"flex items-start\">\n {/* Status indicator */}\n <div className=\"mr-4 mt-1 flex-shrink-0\">\n {index < currentStep ? (\n step.hasError ? (\n // Error indicator - Red X\n <div className=\"flex items-center justify-center w-6 h-6 rounded-full bg-red-500 text-white text-xs\">\n ✕\n </div>\n ) : (\n // Success indicator - Green checkmark\n <div className=\"flex items-center justify-center w-6 h-6 rounded-full bg-[#11E5C5] text-white text-xs\">\n ✓\n </div>\n )\n ) : index === currentStep ? (\n // Loading spinner only for the current step\n <div className=\"w-6 h-6 rounded-full border-2 border-t-[#11E5C5] border-r-[#11E5C5] border-b-[#11E5C5] border-l-transparent animate-spin\"></div>\n ) : (\n // Empty circle for future steps\n <div className=\"w-6 h-6 rounded-full border-2 border-gray-300\"></div>\n )}\n </div>\n\n {/* Step content */}\n <div className=\"flex-1 min-w-0\">\n <p className=\"font-medium text-[#3C3C40] text-sm\">\n {step.title}\n </p>\n <p className=\"text-xs text-gray-500 mt-1\">\n {step.subtitle}\n </p>\n </div>\n </div>\n ))}\n </div>\n </div>\n </div>\n </div>\n </div>\n );\n },\n);\n\nDocumentProcessing.displayName = \"DocumentProcessing\";\n\nexport default DocumentProcessing;\nexport type { DocumentProcessingProps };\n"],"names":["memo","useCallback","useI18n","useState","useRef","useMemo","__awaiter","analyzeFiles","__assign","useEffect","getActiveSessionId","isS3UploadNotConfirmedError","isQuotaExceededError","QUOTA_EXCEEDED_CODE","_jsx","_jsxs","Title","Subtitle"],"mappings":"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"}
@@ -23,7 +23,7 @@ var BeforePhoto = function (_a) {
23
23
  };
24
24
  return (jsxRuntime.jsx(MobilePageLayout.default, { footer: jsxRuntime.jsx(PageActions.default, { primary: jsxRuntime.jsx(Button.default, { onClick: goOnNextStep, children: t("id_check.before.button_ready", "Je suis prêt.e") }), secondary: onBack ? (jsxRuntime.jsx(Button.default, { variant: "secondary", onClick: onBack, children: t("buttons.back") })) : undefined }), children: jsxRuntime.jsx("div", { className: "px-4 py-6 pt-11 md:px-8 md:py-8", children: jsxRuntime.jsxs("div", { className: "w-full max-w-md mx-auto space-y-6", children: [jsxRuntime.jsxs("div", { className: "text-center space-y-4", children: [jsxRuntime.jsx(Title.default, { className: "text-xl md:text-2xl lg:text-3xl", children: isSingleSided
25
25
  ? t("id_check.before.title_single_sided", "Tenez votre document face à la caméra")
26
- : t("id_check.before.title_front", "Tenez le recto de votre document") }), jsxRuntime.jsx(Subtitle.default, { className: "text-sm text-gray-600 leading-relaxed", children: t("id_check.before.subtitle_front", "Afin de vérifier votre document, nous allons vous demander de le tenir face à la caméra") })] }), jsxRuntime.jsx("div", { className: "flex justify-center", children: jsxRuntime.jsx("img", { src: isSingleSided ? beforePassport.default : beforePhoto.default, alt: t("id_check.before.alt_front", "Avant de prendre la photo"), className: "max-w-full h-auto object-contain max-h-[30vh]" }) })] }) }) }));
26
+ : t("id_check.before.title_front", "Recto de votre document") }), jsxRuntime.jsx(Subtitle.default, { className: "text-sm text-gray-600 leading-relaxed", children: t("id_check.before.subtitle_front", "Vous pouvez le tenir face à la caméra ou le poser à plat sur une surface stable, comme vous préférez.") })] }), jsxRuntime.jsx("div", { className: "flex justify-center", children: jsxRuntime.jsx("img", { src: isSingleSided ? beforePassport.default : beforePhoto.default, alt: t("id_check.before.alt_front", "Avant de prendre la photo"), className: "max-w-full h-auto object-contain max-h-[30vh]" }) })] }) }) }));
27
27
  };
28
28
 
29
29
  exports.default = BeforePhoto;
@@ -1 +1 @@
1
- {"version":3,"file":"BeforePhoto.js","sources":["../../../../../src/components/id-check/BeforePhoto.tsx"],"sourcesContent":["import { beforePhotoIcon, beforePassportIcon } from \"../../assets\";\nimport Button from \"../ui/Button\";\nimport PageActions from \"../ui/PageActions\";\nimport Subtitle from \"../ui/Subtitle\";\nimport Title from \"../ui/Title\";\nimport MobilePageLayout from \"../ui/MobilePageLayout\";\nimport { useI18n } from \"../../hooks/useI18n\";\n\ninterface BeforePhotoProps {\n setStep: (step: number) => void;\n isSingleSided?: boolean;\n onBack?: () => void;\n}\n\nconst BeforePhoto = ({ setStep, isSingleSided = false, onBack }: BeforePhotoProps) => {\n const { t } = useI18n();\n\n const goOnNextStep = () => {\n setStep(1);\n };\n\n return (\n <MobilePageLayout\n footer={\n <PageActions\n primary={\n <Button onClick={goOnNextStep}>\n {t(\"id_check.before.button_ready\", \"Je suis prêt.e\")}\n </Button>\n }\n secondary={\n onBack ? (\n <Button variant=\"secondary\" onClick={onBack}>\n {t(\"buttons.back\")}\n </Button>\n ) : undefined\n }\n />\n }\n >\n <div className=\"px-4 py-6 pt-11 md:px-8 md:py-8\">\n <div className=\"w-full max-w-md mx-auto space-y-6\">\n {/* Header */}\n <div className=\"text-center space-y-4\">\n <Title className=\"text-xl md:text-2xl lg:text-3xl\">\n {isSingleSided\n ? t(\"id_check.before.title_single_sided\", \"Tenez votre document face à la caméra\")\n : t(\"id_check.before.title_front\", \"Tenez le recto de votre document\")\n }\n </Title>\n <Subtitle className=\"text-sm text-gray-600 leading-relaxed\">\n {t(\n \"id_check.before.subtitle_front\",\n \"Afin de vérifier votre document, nous allons vous demander de le tenir face à la caméra\"\n )}\n </Subtitle>\n </div>\n\n {/* Illustration */}\n <div className=\"flex justify-center\">\n <img\n src={isSingleSided ? beforePassportIcon : beforePhotoIcon}\n alt={t(\"id_check.before.alt_front\", \"Avant de prendre la photo\")}\n className=\"max-w-full h-auto object-contain max-h-[30vh]\"\n />\n </div>\n </div>\n </div>\n </MobilePageLayout>\n );\n};\n\nexport default BeforePhoto;\n"],"names":["useI18n","_jsx","MobilePageLayout","PageActions","Button","_jsxs","Title","Subtitle","beforePassportIcon","beforePhotoIcon"],"mappings":";;;;;;;;;;;;;;;;;AAcA,IAAM,WAAW,GAAG,UAAC,EAA4D,EAAA;QAA1D,OAAO,GAAA,EAAA,CAAA,OAAA,EAAE,EAAA,GAAA,EAAA,CAAA,aAAqB,EAArB,aAAa,mBAAG,KAAK,GAAA,EAAA,EAAE,MAAM,GAAA,EAAA,CAAA,MAAA;AACnD,IAAA,IAAA,CAAC,GAAKA,eAAO,EAAE,EAAd;AAET,IAAA,IAAM,YAAY,GAAG,YAAA;QACnB,OAAO,CAAC,CAAC,CAAC;AACZ,IAAA,CAAC;AAED,IAAA,QACEC,cAAA,CAACC,wBAAgB,EAAA,EACf,MAAM,EACJD,cAAA,CAACE,mBAAW,EAAA,EACV,OAAO,EACLF,cAAA,CAACG,cAAM,EAAA,EAAC,OAAO,EAAE,YAAY,EAAA,QAAA,EAC1B,CAAC,CAAC,8BAA8B,EAAE,gBAAgB,CAAC,EAAA,CAC7C,EAEX,SAAS,EACP,MAAM,IACJH,eAACG,cAAM,EAAA,EAAC,OAAO,EAAC,WAAW,EAAC,OAAO,EAAE,MAAM,EAAA,QAAA,EACxC,CAAC,CAAC,cAAc,CAAC,EAAA,CACX,IACP,SAAS,EAAA,CAEf,EAAA,QAAA,EAGJH,wBAAK,SAAS,EAAC,iCAAiC,EAAA,QAAA,EAC9CI,yBAAK,SAAS,EAAC,mCAAmC,EAAA,QAAA,EAAA,CAEhDA,yBAAK,SAAS,EAAC,uBAAuB,EAAA,QAAA,EAAA,CACpCJ,eAACK,aAAK,EAAA,EAAC,SAAS,EAAC,iCAAiC,YAC/C;AACC,sCAAE,CAAC,CAAC,oCAAoC,EAAE,uCAAuC;AACjF,sCAAE,CAAC,CAAC,6BAA6B,EAAE,kCAAkC,CAAC,EAAA,CAElE,EACRL,cAAA,CAACM,gBAAQ,EAAA,EAAC,SAAS,EAAC,uCAAuC,EAAA,QAAA,EACxD,CAAC,CACA,gCAAgC,EAChC,yFAAyF,CAC1F,EAAA,CACQ,CAAA,EAAA,CACP,EAGNN,cAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAC,qBAAqB,EAAA,QAAA,EAClCA,cAAA,CAAA,KAAA,EAAA,EACE,GAAG,EAAE,aAAa,GAAGO,sBAAkB,GAAGC,mBAAe,EACzD,GAAG,EAAE,CAAC,CAAC,2BAA2B,EAAE,2BAA2B,CAAC,EAChE,SAAS,EAAC,+CAA+C,EAAA,CACzD,EAAA,CACE,CAAA,EAAA,CACF,EAAA,CACF,EAAA,CACW;AAEvB;;;;"}
1
+ {"version":3,"file":"BeforePhoto.js","sources":["../../../../../src/components/id-check/BeforePhoto.tsx"],"sourcesContent":["import { beforePhotoIcon, beforePassportIcon } from \"../../assets\";\nimport Button from \"../ui/Button\";\nimport PageActions from \"../ui/PageActions\";\nimport Subtitle from \"../ui/Subtitle\";\nimport Title from \"../ui/Title\";\nimport MobilePageLayout from \"../ui/MobilePageLayout\";\nimport { useI18n } from \"../../hooks/useI18n\";\n\ninterface BeforePhotoProps {\n setStep: (step: number) => void;\n isSingleSided?: boolean;\n onBack?: () => void;\n}\n\nconst BeforePhoto = ({ setStep, isSingleSided = false, onBack }: BeforePhotoProps) => {\n const { t } = useI18n();\n\n const goOnNextStep = () => {\n setStep(1);\n };\n\n return (\n <MobilePageLayout\n footer={\n <PageActions\n primary={\n <Button onClick={goOnNextStep}>\n {t(\"id_check.before.button_ready\", \"Je suis prêt.e\")}\n </Button>\n }\n secondary={\n onBack ? (\n <Button variant=\"secondary\" onClick={onBack}>\n {t(\"buttons.back\")}\n </Button>\n ) : undefined\n }\n />\n }\n >\n <div className=\"px-4 py-6 pt-11 md:px-8 md:py-8\">\n <div className=\"w-full max-w-md mx-auto space-y-6\">\n {/* Header */}\n <div className=\"text-center space-y-4\">\n <Title className=\"text-xl md:text-2xl lg:text-3xl\">\n {isSingleSided\n ? t(\"id_check.before.title_single_sided\", \"Tenez votre document face à la caméra\")\n : t(\"id_check.before.title_front\", \"Recto de votre document\")\n }\n </Title>\n <Subtitle className=\"text-sm text-gray-600 leading-relaxed\">\n {t(\n \"id_check.before.subtitle_front\",\n \"Vous pouvez le tenir face à la caméra ou le poser à plat sur une surface stable, comme vous préférez.\"\n )}\n </Subtitle>\n </div>\n\n {/* Illustration */}\n <div className=\"flex justify-center\">\n <img\n src={isSingleSided ? beforePassportIcon : beforePhotoIcon}\n alt={t(\"id_check.before.alt_front\", \"Avant de prendre la photo\")}\n className=\"max-w-full h-auto object-contain max-h-[30vh]\"\n />\n </div>\n </div>\n </div>\n </MobilePageLayout>\n );\n};\n\nexport default BeforePhoto;\n"],"names":["useI18n","_jsx","MobilePageLayout","PageActions","Button","_jsxs","Title","Subtitle","beforePassportIcon","beforePhotoIcon"],"mappings":";;;;;;;;;;;;;;;;;AAcA,IAAM,WAAW,GAAG,UAAC,EAA4D,EAAA;QAA1D,OAAO,GAAA,EAAA,CAAA,OAAA,EAAE,EAAA,GAAA,EAAA,CAAA,aAAqB,EAArB,aAAa,mBAAG,KAAK,GAAA,EAAA,EAAE,MAAM,GAAA,EAAA,CAAA,MAAA;AACnD,IAAA,IAAA,CAAC,GAAKA,eAAO,EAAE,EAAd;AAET,IAAA,IAAM,YAAY,GAAG,YAAA;QACnB,OAAO,CAAC,CAAC,CAAC;AACZ,IAAA,CAAC;AAED,IAAA,QACEC,cAAA,CAACC,wBAAgB,EAAA,EACf,MAAM,EACJD,cAAA,CAACE,mBAAW,EAAA,EACV,OAAO,EACLF,cAAA,CAACG,cAAM,EAAA,EAAC,OAAO,EAAE,YAAY,EAAA,QAAA,EAC1B,CAAC,CAAC,8BAA8B,EAAE,gBAAgB,CAAC,EAAA,CAC7C,EAEX,SAAS,EACP,MAAM,IACJH,eAACG,cAAM,EAAA,EAAC,OAAO,EAAC,WAAW,EAAC,OAAO,EAAE,MAAM,EAAA,QAAA,EACxC,CAAC,CAAC,cAAc,CAAC,EAAA,CACX,IACP,SAAS,EAAA,CAEf,EAAA,QAAA,EAGJH,wBAAK,SAAS,EAAC,iCAAiC,EAAA,QAAA,EAC9CI,yBAAK,SAAS,EAAC,mCAAmC,EAAA,QAAA,EAAA,CAEhDA,yBAAK,SAAS,EAAC,uBAAuB,EAAA,QAAA,EAAA,CACpCJ,eAACK,aAAK,EAAA,EAAC,SAAS,EAAC,iCAAiC,YAC/C;AACC,sCAAE,CAAC,CAAC,oCAAoC,EAAE,uCAAuC;AACjF,sCAAE,CAAC,CAAC,6BAA6B,EAAE,yBAAyB,CAAC,EAAA,CAEzD,EACRL,cAAA,CAACM,gBAAQ,EAAA,EAAC,SAAS,EAAC,uCAAuC,EAAA,QAAA,EACxD,CAAC,CACA,gCAAgC,EAChC,uGAAuG,CACxG,EAAA,CACQ,CAAA,EAAA,CACP,EAGNN,cAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAC,qBAAqB,EAAA,QAAA,EAClCA,cAAA,CAAA,KAAA,EAAA,EACE,GAAG,EAAE,aAAa,GAAGO,sBAAkB,GAAGC,mBAAe,EACzD,GAAG,EAAE,CAAC,CAAC,2BAA2B,EAAE,2BAA2B,CAAC,EAChE,SAAS,EAAC,+CAA+C,EAAA,CACzD,EAAA,CACE,CAAA,EAAA,CACF,EAAA,CACF,EAAA,CACW;AAEvB;;;;"}
@@ -8,9 +8,9 @@ var useI18n = require('../../hooks/useI18n.js');
8
8
 
9
9
  var COUNTDOWN_DURATION = 3; // seconds
10
10
  var CameraMask = function (_a) {
11
- var _b = _a.isPortrait, isPortrait = _b === void 0 ? false : _b, _c = _a.facingMode, facingMode = _c === void 0 ? "environment" : _c, _d = _a.isCountingDown, isCountingDown = _d === void 0 ? false : _d, _e = _a.countdown, countdown = _e === void 0 ? 0 : _e, _f = _a.maskType, maskType = _f === void 0 ? "id" : _f, onMaskRectChange = _a.onMaskRectChange;
12
- var _g = React.useState(null), videoOrientation = _g[0], setVideoOrientation = _g[1];
13
- useI18n.useI18n().t;
11
+ var _b = _a.isPortrait, isPortrait = _b === void 0 ? false : _b, _c = _a.facingMode, facingMode = _c === void 0 ? "environment" : _c, _d = _a.isCountingDown, isCountingDown = _d === void 0 ? false : _d, _e = _a.countdown, countdown = _e === void 0 ? 0 : _e, _f = _a.maskType, maskType = _f === void 0 ? "id" : _f, _g = _a.documentDetected, documentDetected = _g === void 0 ? false : _g, qualityHint = _a.qualityHint, onMaskRectChange = _a.onMaskRectChange;
12
+ var _h = React.useState(null), videoOrientation = _h[0], setVideoOrientation = _h[1];
13
+ var t = useI18n.useI18n().t;
14
14
  var maskRef = React.useRef(null);
15
15
  // Détecter l'orientation réelle de la vidéo pour synchroniser avec Photo.tsx
16
16
  React.useEffect(function () {
@@ -78,10 +78,16 @@ var CameraMask = function (_a) {
78
78
  boxShadow: "0 0 0 9999px rgba(0, 0, 0, 0.75)",
79
79
  borderRadius: "8px",
80
80
  width: maskType === "document" ? (effectiveIsPortrait ? "85%" : "60%") : (effectiveIsPortrait ? "95%" : "85%"),
81
- }, "data-mask": maskType === "document" ? "document-frame" : "id-frame", ref: maskRef, children: [jsxRuntime.jsx("div", { className: "absolute inset-0", style: {
82
- border: "3px solid white",
81
+ }, "data-mask": maskType === "document" ? "document-frame" : "id-frame", ref: maskRef, children: [jsxRuntime.jsx("div", { "data-testid": "camera-mask-border", className: "absolute inset-0 transition-colors duration-300 ease-in-out", style: {
82
+ borderWidth: "3px",
83
+ borderColor: documentDetected
84
+ ? "#11E5C5"
85
+ : qualityHint === "too_dark" || qualityHint === "too_bright"
86
+ ? "#F59E0B"
87
+ : "#ffffff",
88
+ borderStyle: documentDetected ? "solid" : "dashed",
83
89
  borderRadius: "8px",
84
- } }), isCountingDown && (jsxRuntime.jsx("svg", { className: "absolute inset-0 w-full h-full", style: {
90
+ } }), documentDetected && (jsxRuntime.jsxs("div", { "data-testid": "document-detected-badge", className: "absolute -top-9 left-1/2 -translate-x-1/2 flex items-center gap-1 bg-[#11E5C5] text-white text-xs font-semibold px-2 py-1 rounded-full whitespace-nowrap", children: [jsxRuntime.jsx("span", { "aria-hidden": "true", children: "\u2713" }), jsxRuntime.jsx("span", { children: t("id_check.camera.document_detected", "Document détecté") })] })), isCountingDown && (jsxRuntime.jsx("svg", { className: "absolute inset-0 w-full h-full", style: {
85
91
  borderRadius: "8px",
86
92
  }, children: jsxRuntime.jsx("rect", { x: "1.5", y: "1.5", width: "calc(100% - 3px)", height: "calc(100% - 3px)", fill: "none", stroke: "#11E5C5", strokeWidth: "3", strokeDasharray: "100", strokeDashoffset: 100 - progress * 100, pathLength: "100", rx: "6", ry: "6", style: {
87
93
  transition: "stroke-dashoffset 0.1s linear",
@@ -1 +1 @@
1
- {"version":3,"file":"CameraMask.js","sources":["../../../../../src/components/id-check/CameraMask.tsx"],"sourcesContent":["import React, { useEffect, useLayoutEffect, useRef, useState } from \"react\";\nimport { useI18n } from \"../../hooks/useI18n\";\nimport type { CameraFacingMode } from \"../../types/camera\";\n\ninterface CameraMaskProps {\n isDetecting: boolean;\n isPortrait?: boolean;\n facingMode?: CameraFacingMode;\n isCountingDown?: boolean;\n countdown?: number;\n maskType?: \"id\" | \"document\";\n onMaskRectChange?: (\n rect:\n | {\n left: number;\n top: number;\n width: number;\n height: number;\n }\n | null\n ) => void;\n}\n\nconst COUNTDOWN_DURATION = 3; // seconds\n\nconst CameraMask: React.FC<CameraMaskProps> = ({\n isPortrait = false,\n facingMode = \"environment\",\n isCountingDown = false,\n countdown = 0,\n maskType = \"id\",\n onMaskRectChange,\n}) => {\n const [videoOrientation, setVideoOrientation] = useState<boolean | null>(\n null\n );\n const { t } = useI18n();\n const maskRef = useRef<HTMLDivElement>(null);\n\n // Détecter l'orientation réelle de la vidéo pour synchroniser avec Photo.tsx\n useEffect(() => {\n const checkVideoOrientation = () => {\n const videoElement = document.querySelector(\"video\");\n if (\n videoElement &&\n videoElement.videoWidth > 0 &&\n videoElement.videoHeight > 0\n ) {\n const videoIsPortrait =\n videoElement.videoHeight > videoElement.videoWidth;\n setVideoOrientation(videoIsPortrait);\n }\n };\n\n // Vérifier immédiatement\n checkVideoOrientation();\n\n // Vérifier périodiquement en cas de changement\n const interval = setInterval(checkVideoOrientation, 1000);\n\n return () => clearInterval(interval);\n }, []);\n\n // Utiliser l'orientation de la vidéo si disponible, sinon fallback sur l'orientation de l'écran\n const effectiveIsPortrait =\n videoOrientation !== null ? videoOrientation : isPortrait;\n\n // Calculate the progress of the animation (0 to 1) over COUNTDOWN_DURATION seconds\n const progress = isCountingDown\n ? Math.min(1, (COUNTDOWN_DURATION - countdown) / COUNTDOWN_DURATION)\n : 0;\n\n useLayoutEffect(() => {\n if (!onMaskRectChange) {\n return;\n }\n\n const notify = () => {\n if (!maskRef.current) {\n onMaskRectChange(null);\n console.debug(\"[CameraMask] maskRef unavailable\");\n return;\n }\n\n const rect = maskRef.current.getBoundingClientRect();\n onMaskRectChange({\n left: rect.left,\n top: rect.top,\n width: rect.width,\n height: rect.height,\n });\n console.debug(\"[CameraMask] mask rect updated\", {\n left: rect.left,\n top: rect.top,\n width: rect.width,\n height: rect.height,\n });\n };\n\n requestAnimationFrame(notify);\n\n const handleResize = () => requestAnimationFrame(notify);\n window.addEventListener(\"resize\", handleResize);\n\n let resizeObserver: ResizeObserver | null = null;\n if (typeof ResizeObserver !== \"undefined\" && maskRef.current) {\n resizeObserver = new ResizeObserver(() => {\n requestAnimationFrame(notify);\n });\n resizeObserver.observe(maskRef.current);\n }\n\n return () => {\n window.removeEventListener(\"resize\", handleResize);\n resizeObserver?.disconnect();\n };\n }, [onMaskRectChange, effectiveIsPortrait, facingMode]);\n\n return (\n <div className=\"absolute inset-0 z-10 flex items-center justify-center\">\n <div\n className={`\n relative\n ${maskType === \"document\" ? \"aspect-[1/1.41]\" : \"aspect-3/2\"}\n ${facingMode === \"user\" ? \"transform scale-x-[-1]\" : \"\"}\n transition-all duration-300 ease-in-out\n `}\n style={{\n boxShadow: \"0 0 0 9999px rgba(0, 0, 0, 0.75)\",\n borderRadius: \"8px\",\n width: maskType === \"document\" ? (effectiveIsPortrait ? \"85%\" : \"60%\") : (effectiveIsPortrait ? \"95%\" : \"85%\"),\n }}\n data-mask={maskType === \"document\" ? \"document-frame\" : \"id-frame\"}\n ref={maskRef}\n >\n {/* Base white border */}\n <div\n className=\"absolute inset-0\"\n style={{\n border: \"3px solid white\",\n borderRadius: \"8px\",\n }}\n />\n\n {/* Animated blue border overlay */}\n {isCountingDown && (\n <svg\n className=\"absolute inset-0 w-full h-full\"\n style={{\n borderRadius: \"8px\",\n }}\n >\n <rect\n x=\"1.5\"\n y=\"1.5\"\n width={`calc(100% - 3px)`}\n height={`calc(100% - 3px)`}\n fill=\"none\"\n stroke=\"#11E5C5\"\n strokeWidth=\"3\"\n strokeDasharray=\"100\"\n strokeDashoffset={100 - progress * 100}\n pathLength=\"100\"\n rx=\"6\"\n ry=\"6\"\n style={{\n transition: \"stroke-dashoffset 0.1s linear\",\n }}\n />\n </svg>\n )}\n\n {/* Countdown display */}\n {isCountingDown && (\n <div className=\"absolute inset-0 flex items-center justify-center\">\n <div className=\"bg-black bg-opacity-50 text-white px-4 py-2 rounded-full text-lg font-bold\">\n {Math.ceil(countdown)}\n </div>\n </div>\n )}\n\n {/* Indicateur d'orientation pour debug */}\n {(typeof process !== \"undefined\" && process.env && process.env.NODE_ENV === \"development\") && (\n <div className=\"absolute -top-8 left-0 text-white text-xs bg-black bg-opacity-50 px-2 py-1 rounded\">\n {effectiveIsPortrait ? t(\"id_check.orientation.portrait\", \"Portrait\") : t(\"id_check.orientation.landscape\", \"Landscape\")}\n {videoOrientation !== null ? ` (${t(\"id_check.orientation.video_suffix\", \"Video\")})` : ` (${t(\"id_check.orientation.screen_suffix\", \"Screen\")})`}\n </div>\n )}\n </div>\n </div>\n );\n};\n\nexport default CameraMask;\n"],"names":["useState","useI18n","useRef","useEffect","useLayoutEffect","_jsx","_jsxs"],"mappings":";;;;;;;;AAuBA,IAAM,kBAAkB,GAAG,CAAC,CAAC;AAE7B,IAAM,UAAU,GAA8B,UAAC,EAO9C,EAAA;AANC,IAAA,IAAA,EAAA,GAAA,EAAA,CAAA,UAAkB,EAAlB,UAAU,GAAA,EAAA,KAAA,MAAA,GAAG,KAAK,KAAA,EAClB,EAAA,GAAA,EAAA,CAAA,UAA0B,EAA1B,UAAU,mBAAG,aAAa,GAAA,EAAA,EAC1B,EAAA,GAAA,EAAA,CAAA,cAAsB,EAAtB,cAAc,GAAA,EAAA,KAAA,MAAA,GAAG,KAAK,GAAA,EAAA,EACtB,EAAA,GAAA,EAAA,CAAA,SAAa,EAAb,SAAS,GAAA,EAAA,KAAA,MAAA,GAAG,CAAC,GAAA,EAAA,EACb,EAAA,GAAA,EAAA,CAAA,QAAe,EAAf,QAAQ,GAAA,EAAA,KAAA,MAAA,GAAG,IAAI,GAAA,EAAA,EACf,gBAAgB,GAAA,EAAA,CAAA,gBAAA;IAEV,IAAA,EAAA,GAA0CA,cAAQ,CACtD,IAAI,CACL,EAFM,gBAAgB,GAAA,EAAA,CAAA,CAAA,CAAA,EAAE,mBAAmB,GAAA,EAAA,CAAA,CAAA,CAE3C;AACO,IAAMC,eAAO,EAAE;AACvB,IAAA,IAAM,OAAO,GAAGC,YAAM,CAAiB,IAAI,CAAC;;AAG5C,IAAAC,eAAS,CAAC,YAAA;AACR,QAAA,IAAM,qBAAqB,GAAG,YAAA;YAC5B,IAAM,YAAY,GAAG,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC;AACpD,YAAA,IACE,YAAY;gBACZ,YAAY,CAAC,UAAU,GAAG,CAAC;AAC3B,gBAAA,YAAY,CAAC,WAAW,GAAG,CAAC,EAC5B;gBACA,IAAM,eAAe,GACnB,YAAY,CAAC,WAAW,GAAG,YAAY,CAAC,UAAU;gBACpD,mBAAmB,CAAC,eAAe,CAAC;YACtC;AACF,QAAA,CAAC;;AAGD,QAAA,qBAAqB,EAAE;;QAGvB,IAAM,QAAQ,GAAG,WAAW,CAAC,qBAAqB,EAAE,IAAI,CAAC;QAEzD,OAAO,YAAA,EAAM,OAAA,aAAa,CAAC,QAAQ,CAAC,CAAA,CAAvB,CAAuB;IACtC,CAAC,EAAE,EAAE,CAAC;;AAGN,IAAA,IAAM,mBAAmB,GACvB,gBAAgB,KAAK,IAAI,GAAG,gBAAgB,GAAG,UAAU;;IAG3D,IAAM,QAAQ,GAAG;AACf,UAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,kBAAkB,GAAG,SAAS,IAAI,kBAAkB;UACjE,CAAC;AAEL,IAAAC,qBAAe,CAAC,YAAA;QACd,IAAI,CAAC,gBAAgB,EAAE;YACrB;QACF;AAEA,QAAA,IAAM,MAAM,GAAG,YAAA;AACb,YAAA,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE;gBACpB,gBAAgB,CAAC,IAAI,CAAC;AACtB,gBAAA,OAAO,CAAC,KAAK,CAAC,kCAAkC,CAAC;gBACjD;YACF;YAEA,IAAM,IAAI,GAAG,OAAO,CAAC,OAAO,CAAC,qBAAqB,EAAE;AACpD,YAAA,gBAAgB,CAAC;gBACf,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,GAAG,EAAE,IAAI,CAAC,GAAG;gBACb,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,MAAM,EAAE,IAAI,CAAC,MAAM;AACpB,aAAA,CAAC;AACF,YAAA,OAAO,CAAC,KAAK,CAAC,gCAAgC,EAAE;gBAC9C,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,GAAG,EAAE,IAAI,CAAC,GAAG;gBACb,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,MAAM,EAAE,IAAI,CAAC,MAAM;AACpB,aAAA,CAAC;AACJ,QAAA,CAAC;QAED,qBAAqB,CAAC,MAAM,CAAC;QAE7B,IAAM,YAAY,GAAG,YAAA,EAAM,OAAA,qBAAqB,CAAC,MAAM,CAAC,CAAA,CAA7B,CAA6B;AACxD,QAAA,MAAM,CAAC,gBAAgB,CAAC,QAAQ,EAAE,YAAY,CAAC;QAE/C,IAAI,cAAc,GAA0B,IAAI;QAChD,IAAI,OAAO,cAAc,KAAK,WAAW,IAAI,OAAO,CAAC,OAAO,EAAE;YAC5D,cAAc,GAAG,IAAI,cAAc,CAAC,YAAA;gBAClC,qBAAqB,CAAC,MAAM,CAAC;AAC/B,YAAA,CAAC,CAAC;AACF,YAAA,cAAc,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC;QACzC;QAEA,OAAO,YAAA;AACL,YAAA,MAAM,CAAC,mBAAmB,CAAC,QAAQ,EAAE,YAAY,CAAC;AAClD,YAAA,cAAc,aAAd,cAAc,KAAA,MAAA,GAAA,MAAA,GAAd,cAAc,CAAE,UAAU,EAAE;AAC9B,QAAA,CAAC;IACH,CAAC,EAAE,CAAC,gBAAgB,EAAE,mBAAmB,EAAE,UAAU,CAAC,CAAC;AAEvD,IAAA,QACEC,cAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAC,wDAAwD,YACrEC,eAAA,CAAA,KAAA,EAAA,EACE,SAAS,EAAE,kCAAA,CAAA,MAAA,CAEP,QAAQ,KAAK,UAAU,GAAG,iBAAiB,GAAG,YAAY,EAAA,cAAA,CAAA,CAAA,MAAA,CAC1D,UAAU,KAAK,MAAM,GAAG,wBAAwB,GAAG,EAAE,EAAA,+DAAA,CAExD,EACD,KAAK,EAAE;AACL,gBAAA,SAAS,EAAE,kCAAkC;AAC7C,gBAAA,YAAY,EAAE,KAAK;AACnB,gBAAA,KAAK,EAAE,QAAQ,KAAK,UAAU,IAAI,mBAAmB,GAAG,KAAK,GAAG,KAAK,KAAK,mBAAmB,GAAG,KAAK,GAAG,KAAK,CAAC;aAC/G,EAAA,WAAA,EACU,QAAQ,KAAK,UAAU,GAAG,gBAAgB,GAAG,UAAU,EAClE,GAAG,EAAE,OAAO,EAAA,QAAA,EAAA,CAGZD,cAAA,CAAA,KAAA,EAAA,EACE,SAAS,EAAC,kBAAkB,EAC5B,KAAK,EAAE;AACL,wBAAA,MAAM,EAAE,iBAAiB;AACzB,wBAAA,YAAY,EAAE,KAAK;qBACpB,EAAA,CACD,EAGD,cAAc,KACbA,cAAA,CAAA,KAAA,EAAA,EACE,SAAS,EAAC,gCAAgC,EAC1C,KAAK,EAAE;AACL,wBAAA,YAAY,EAAE,KAAK;qBACpB,EAAA,QAAA,EAEDA,cAAA,CAAA,MAAA,EAAA,EACE,CAAC,EAAC,KAAK,EACP,CAAC,EAAC,KAAK,EACP,KAAK,EAAE,kBAAkB,EACzB,MAAM,EAAE,kBAAkB,EAC1B,IAAI,EAAC,MAAM,EACX,MAAM,EAAC,SAAS,EAChB,WAAW,EAAC,GAAG,EACf,eAAe,EAAC,KAAK,EACrB,gBAAgB,EAAE,GAAG,GAAG,QAAQ,GAAG,GAAG,EACtC,UAAU,EAAC,KAAK,EAChB,EAAE,EAAC,GAAG,EACN,EAAE,EAAC,GAAG,EACN,KAAK,EAAE;AACL,4BAAA,UAAU,EAAE,+BAA+B;yBAC5C,EAAA,CACD,EAAA,CACE,CACP,EAGA,cAAc,KACbA,cAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAC,mDAAmD,EAAA,QAAA,EAChEA,wBAAK,SAAS,EAAC,4EAA4E,EAAA,QAAA,EACxF,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,EAAA,CACjB,EAAA,CACF,CACP,EAGA,CAAC,OAAO,OAAO,KAAK,WAAW,IAAI,OAAO,CAAC,GAAG,IAAI,YAAoB,KAAK,aAAa,CAKxF,CAAA,EAAA,CACG,EAAA,CACF;AAEV;;;;"}
1
+ {"version":3,"file":"CameraMask.js","sources":["../../../../../src/components/id-check/CameraMask.tsx"],"sourcesContent":["import React, { useEffect, useLayoutEffect, useRef, useState } from \"react\";\nimport { useI18n } from \"../../hooks/useI18n\";\nimport type { CameraFacingMode } from \"../../types/camera\";\nimport type { DocumentDetectionHint } from \"../../services/documentEdgeDetectionService\";\n\ninterface CameraMaskProps {\n isDetecting: boolean;\n isPortrait?: boolean;\n facingMode?: CameraFacingMode;\n isCountingDown?: boolean;\n countdown?: number;\n maskType?: \"id\" | \"document\";\n documentDetected?: boolean;\n qualityHint?: DocumentDetectionHint;\n onMaskRectChange?: (\n rect:\n | {\n left: number;\n top: number;\n width: number;\n height: number;\n }\n | null\n ) => void;\n}\n\nconst COUNTDOWN_DURATION = 3; // seconds\n\nconst CameraMask: React.FC<CameraMaskProps> = ({\n isPortrait = false,\n facingMode = \"environment\",\n isCountingDown = false,\n countdown = 0,\n maskType = \"id\",\n documentDetected = false,\n qualityHint,\n onMaskRectChange,\n}) => {\n const [videoOrientation, setVideoOrientation] = useState<boolean | null>(\n null\n );\n const { t } = useI18n();\n const maskRef = useRef<HTMLDivElement>(null);\n\n // Détecter l'orientation réelle de la vidéo pour synchroniser avec Photo.tsx\n useEffect(() => {\n const checkVideoOrientation = () => {\n const videoElement = document.querySelector(\"video\");\n if (\n videoElement &&\n videoElement.videoWidth > 0 &&\n videoElement.videoHeight > 0\n ) {\n const videoIsPortrait =\n videoElement.videoHeight > videoElement.videoWidth;\n setVideoOrientation(videoIsPortrait);\n }\n };\n\n // Vérifier immédiatement\n checkVideoOrientation();\n\n // Vérifier périodiquement en cas de changement\n const interval = setInterval(checkVideoOrientation, 1000);\n\n return () => clearInterval(interval);\n }, []);\n\n // Utiliser l'orientation de la vidéo si disponible, sinon fallback sur l'orientation de l'écran\n const effectiveIsPortrait =\n videoOrientation !== null ? videoOrientation : isPortrait;\n\n // Calculate the progress of the animation (0 to 1) over COUNTDOWN_DURATION seconds\n const progress = isCountingDown\n ? Math.min(1, (COUNTDOWN_DURATION - countdown) / COUNTDOWN_DURATION)\n : 0;\n\n useLayoutEffect(() => {\n if (!onMaskRectChange) {\n return;\n }\n\n const notify = () => {\n if (!maskRef.current) {\n onMaskRectChange(null);\n console.debug(\"[CameraMask] maskRef unavailable\");\n return;\n }\n\n const rect = maskRef.current.getBoundingClientRect();\n onMaskRectChange({\n left: rect.left,\n top: rect.top,\n width: rect.width,\n height: rect.height,\n });\n console.debug(\"[CameraMask] mask rect updated\", {\n left: rect.left,\n top: rect.top,\n width: rect.width,\n height: rect.height,\n });\n };\n\n requestAnimationFrame(notify);\n\n const handleResize = () => requestAnimationFrame(notify);\n window.addEventListener(\"resize\", handleResize);\n\n let resizeObserver: ResizeObserver | null = null;\n if (typeof ResizeObserver !== \"undefined\" && maskRef.current) {\n resizeObserver = new ResizeObserver(() => {\n requestAnimationFrame(notify);\n });\n resizeObserver.observe(maskRef.current);\n }\n\n return () => {\n window.removeEventListener(\"resize\", handleResize);\n resizeObserver?.disconnect();\n };\n }, [onMaskRectChange, effectiveIsPortrait, facingMode]);\n\n return (\n <div className=\"absolute inset-0 z-10 flex items-center justify-center\">\n <div\n className={`\n relative\n ${maskType === \"document\" ? \"aspect-[1/1.41]\" : \"aspect-3/2\"}\n ${facingMode === \"user\" ? \"transform scale-x-[-1]\" : \"\"}\n transition-all duration-300 ease-in-out\n `}\n style={{\n boxShadow: \"0 0 0 9999px rgba(0, 0, 0, 0.75)\",\n borderRadius: \"8px\",\n width: maskType === \"document\" ? (effectiveIsPortrait ? \"85%\" : \"60%\") : (effectiveIsPortrait ? \"95%\" : \"85%\"),\n }}\n data-mask={maskType === \"document\" ? \"document-frame\" : \"id-frame\"}\n ref={maskRef}\n >\n {/* Base border - white dashed by default, amber dashed on a fixable\n quality issue (too dark / too much glare), green solid once a\n document is detected. Never conveys a blocked state — always\n informative, never a gate. */}\n <div\n data-testid=\"camera-mask-border\"\n className=\"absolute inset-0 transition-colors duration-300 ease-in-out\"\n style={{\n borderWidth: \"3px\",\n borderColor: documentDetected\n ? \"#11E5C5\"\n : qualityHint === \"too_dark\" || qualityHint === \"too_bright\"\n ? \"#F59E0B\"\n : \"#ffffff\",\n borderStyle: documentDetected ? \"solid\" : \"dashed\",\n borderRadius: \"8px\",\n }}\n />\n\n {/* Document detected badge - icon + text, not color-only */}\n {documentDetected && (\n <div\n data-testid=\"document-detected-badge\"\n className=\"absolute -top-9 left-1/2 -translate-x-1/2 flex items-center gap-1 bg-[#11E5C5] text-white text-xs font-semibold px-2 py-1 rounded-full whitespace-nowrap\"\n >\n <span aria-hidden=\"true\">✓</span>\n <span>\n {t(\"id_check.camera.document_detected\", \"Document détecté\")}\n </span>\n </div>\n )}\n\n {/* Animated blue border overlay */}\n {isCountingDown && (\n <svg\n className=\"absolute inset-0 w-full h-full\"\n style={{\n borderRadius: \"8px\",\n }}\n >\n <rect\n x=\"1.5\"\n y=\"1.5\"\n width={`calc(100% - 3px)`}\n height={`calc(100% - 3px)`}\n fill=\"none\"\n stroke=\"#11E5C5\"\n strokeWidth=\"3\"\n strokeDasharray=\"100\"\n strokeDashoffset={100 - progress * 100}\n pathLength=\"100\"\n rx=\"6\"\n ry=\"6\"\n style={{\n transition: \"stroke-dashoffset 0.1s linear\",\n }}\n />\n </svg>\n )}\n\n {/* Countdown display */}\n {isCountingDown && (\n <div className=\"absolute inset-0 flex items-center justify-center\">\n <div className=\"bg-black bg-opacity-50 text-white px-4 py-2 rounded-full text-lg font-bold\">\n {Math.ceil(countdown)}\n </div>\n </div>\n )}\n\n {/* Indicateur d'orientation pour debug */}\n {(typeof process !== \"undefined\" && process.env && process.env.NODE_ENV === \"development\") && (\n <div className=\"absolute -top-8 left-0 text-white text-xs bg-black bg-opacity-50 px-2 py-1 rounded\">\n {effectiveIsPortrait ? t(\"id_check.orientation.portrait\", \"Portrait\") : t(\"id_check.orientation.landscape\", \"Landscape\")}\n {videoOrientation !== null ? ` (${t(\"id_check.orientation.video_suffix\", \"Video\")})` : ` (${t(\"id_check.orientation.screen_suffix\", \"Screen\")})`}\n </div>\n )}\n </div>\n </div>\n );\n};\n\nexport default CameraMask;\n"],"names":["useState","useI18n","useRef","useEffect","useLayoutEffect","_jsx","_jsxs"],"mappings":"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"}
@@ -6,7 +6,7 @@ var tslib_es6 = require('../../node_modules/tslib/tslib.es6.js');
6
6
  var jsxRuntime = require('react/jsx-runtime');
7
7
  var React = require('react');
8
8
  var cameraService = require('../../services/cameraService.js');
9
- var documentDetectionService = require('../../services/documentDetectionService.js');
9
+ var documentEdgeDetectionService = require('../../services/documentEdgeDetectionService.js');
10
10
  var CameraError = require('./CameraError.js');
11
11
  var CameraMask = require('./CameraMask.js');
12
12
  var OrientationToggle = require('./OrientationToggle.js');
@@ -23,13 +23,29 @@ var Photo = function (_a) {
23
23
  var _g = React.useState(window.matchMedia("(orientation: portrait)").matches), isPortrait = _g[0], setIsPortrait = _g[1];
24
24
  var _h = React.useState(false), isCountingDown = _h[0], setIsCountingDown = _h[1];
25
25
  var _j = React.useState(0), countdown = _j[0], setCountdown = _j[1];
26
- var _k = React.useState(false); _k[0]; var setDocumentDetected = _k[1];
27
- var _l = React.useState(null), captureError = _l[0], setCaptureError = _l[1];
26
+ var _k = React.useState(false), documentDetected = _k[0], setDocumentDetected = _k[1];
27
+ var _l = React.useState(undefined), qualityHint = _l[0], setQualityHint = _l[1];
28
+ var _m = React.useState(true), isInitializing = _m[0], setIsInitializing = _m[1];
29
+ var _o = React.useState(false), isProcessing = _o[0], setIsProcessing = _o[1];
30
+ var _p = React.useState(null), captureError = _p[0], setCaptureError = _p[1];
31
+ var _q = React.useState(false), showCaptureFlash = _q[0], setShowCaptureFlash = _q[1];
28
32
  var t = useI18n.useI18n().t;
29
- var _m = React.useState(null); _m[0]; var setLastCaptureMeta = _m[1];
30
- var _o = React.useState(null), maskRect = _o[0], setMaskRect = _o[1];
33
+ var _r = React.useState(null); _r[0]; var setLastCaptureMeta = _r[1];
34
+ var _s = React.useState(null), maskRect = _s[0], setMaskRect = _s[1];
31
35
  var countdownIntervalRef = React.useRef(null);
32
36
  var detectionHandlerRef = React.useRef(null);
37
+ var flashTimeoutRef = React.useRef(null);
38
+ var CAPTURE_FLASH_DURATION_MS = 300;
39
+ var triggerCaptureFeedback = React.useCallback(function () {
40
+ if (flashTimeoutRef.current) {
41
+ clearTimeout(flashTimeoutRef.current);
42
+ }
43
+ setShowCaptureFlash(true);
44
+ flashTimeoutRef.current = window.setTimeout(function () {
45
+ setShowCaptureFlash(false);
46
+ flashTimeoutRef.current = null;
47
+ }, CAPTURE_FLASH_DURATION_MS);
48
+ }, []);
33
49
  var CAPTURE_COUNTDOWN_SECONDS = 3;
34
50
  var clearCountdown = React.useCallback(function () {
35
51
  if (countdownIntervalRef.current) {
@@ -54,7 +70,7 @@ var Photo = function (_a) {
54
70
  detectionHandlerRef.current.stop();
55
71
  detectionHandlerRef.current = null;
56
72
  }
57
- documentDetectionService.documentDetectionService.stopDocumentDetection();
73
+ documentEdgeDetectionService.documentEdgeDetectionService.stopDocumentDetection();
58
74
  }, []);
59
75
  var captureDocument = React.useCallback(function () { return tslib_es6.__awaiter(void 0, void 0, void 0, function () {
60
76
  var video, cropArea, videoRect, videoAspect, displayAspect, displayedWidth, displayedHeight, offsetX, offsetY, scaleX, scaleY, cropX, cropY, cropWidth, cropHeight, result, error_1;
@@ -116,6 +132,14 @@ var Photo = function (_a) {
116
132
  }
117
133
  setCaptureError(null);
118
134
  setIsDetecting(false);
135
+ // Reste vrai jusqu'au démontage du composant (changement d'étape côté
136
+ // DocumentCheck) en cas de succès : le traitement post-capture côté
137
+ // parent (redimensionnement, conversion) continue pendant que ce
138
+ // composant est encore affiché, donc l'overlay doit rester visible
139
+ // jusqu'à ce que l'écran change réellement — jamais un écran figé sans
140
+ // feedback.
141
+ setIsProcessing(true);
142
+ triggerCaptureFeedback();
119
143
  _a.label = 1;
120
144
  case 1:
121
145
  _a.trys.push([1, 3, , 4]);
@@ -142,11 +166,12 @@ var Photo = function (_a) {
142
166
  console.error("Document capture failed", error_1);
143
167
  setCaptureError(t("id_check.photo.capture_failed", "La capture a échoué. Veuillez réessayer."));
144
168
  setIsDetecting(true);
169
+ setIsProcessing(false);
145
170
  throw error_1;
146
171
  case 4: return [2 /*return*/];
147
172
  }
148
173
  });
149
- }); }, [facingMode, onCapture, stopDetection, maskRect, t]);
174
+ }); }, [facingMode, onCapture, stopDetection, maskRect, t, triggerCaptureFeedback]);
150
175
  var startCountdown = React.useCallback(function () {
151
176
  if (isCountingDown)
152
177
  return;
@@ -169,11 +194,16 @@ var Photo = function (_a) {
169
194
  }, 100);
170
195
  }, [captureDocument, isCountingDown]);
171
196
  var handleDetectionChange = React.useCallback(function (result) {
172
- setDocumentDetected(result.hasDocument);
197
+ // isStable (pas juste hasDocument) pilote le feedback visuel et
198
+ // l'auto-capture pour éviter le clignotement : une frame isolée où le
199
+ // document "apparaît" ne suffit pas, il faut plusieurs détections
200
+ // consécutives cohérentes.
201
+ setDocumentDetected(result.hasDocument && result.isStable);
202
+ setQualityHint(result.hint);
173
203
  if (!automaticCapture) {
174
204
  return;
175
205
  }
176
- if (result.hasDocument) {
206
+ if (result.hasDocument && result.isStable) {
177
207
  startCountdown();
178
208
  }
179
209
  else {
@@ -192,6 +222,7 @@ var Photo = function (_a) {
192
222
  switch (_a.label) {
193
223
  case 0:
194
224
  setCameraError(null);
225
+ setIsInitializing(true);
195
226
  return [4 /*yield*/, cameraService.cameraService.startCamera(videoRef.current, {
196
227
  facingMode: facingMode,
197
228
  idealResolution: { width: 4096, height: 3072 },
@@ -201,11 +232,14 @@ var Photo = function (_a) {
201
232
  result = _a.sent();
202
233
  if (isCancelled)
203
234
  return [2 /*return*/];
235
+ setIsInitializing(false);
204
236
  if (result.success) {
205
237
  setLastCaptureMeta(null);
206
- if (automaticCapture) {
207
- setIsDetecting(true);
208
- }
238
+ // Detection also runs in manual mode: it only drives the visual
239
+ // feedback on the mask (documentDetected), it never starts the
240
+ // auto-capture countdown unless automaticCapture is true (see
241
+ // handleDetectionChange).
242
+ setIsDetecting(true);
209
243
  }
210
244
  else if (result.error) {
211
245
  setCameraError(result.error);
@@ -222,6 +256,10 @@ var Photo = function (_a) {
222
256
  stopDetection();
223
257
  cameraService.cameraService.stopCamera();
224
258
  setIsDetecting(false);
259
+ if (flashTimeoutRef.current) {
260
+ clearTimeout(flashTimeoutRef.current);
261
+ flashTimeoutRef.current = null;
262
+ }
225
263
  };
226
264
  }, [facingMode, clearCountdown, stopDetection]);
227
265
  React.useEffect(function () {
@@ -229,8 +267,8 @@ var Photo = function (_a) {
229
267
  return;
230
268
  }
231
269
  var cancelled = false;
232
- documentDetectionService.documentDetectionService
233
- .startDocumentDetection(videoRef.current, function (result) {
270
+ documentEdgeDetectionService.documentEdgeDetectionService
271
+ .startDocumentDetection(videoRef.current, null, function (result) {
234
272
  if (!cancelled) {
235
273
  handleDetectionChange(result);
236
274
  }
@@ -263,6 +301,7 @@ var Photo = function (_a) {
263
301
  cancelCaptureTimer();
264
302
  setCaptureError(null);
265
303
  setIsDetecting(false);
304
+ setIsInitializing(true);
266
305
  stopDetection();
267
306
  cameraService.cameraService.stopCamera();
268
307
  cameraService.cameraService
@@ -272,6 +311,7 @@ var Photo = function (_a) {
272
311
  preferImageCapture: true,
273
312
  })
274
313
  .then(function (result) {
314
+ setIsInitializing(false);
275
315
  if (result.success) {
276
316
  setIsDetecting(true);
277
317
  }
@@ -281,22 +321,30 @@ var Photo = function (_a) {
281
321
  });
282
322
  };
283
323
  var handleManualCapture = function () {
324
+ if (isProcessing) {
325
+ return;
326
+ }
284
327
  clearCountdown();
285
328
  captureDocument().catch(function () {
286
329
  // captureDocument already logs and sets error state
287
330
  });
288
331
  };
332
+ var statusMessage = qualityHint === "too_dark"
333
+ ? t("id_check.photo.hint.too_dark", "Trop sombre, cherchez plus de lumière")
334
+ : qualityHint === "too_bright"
335
+ ? t("id_check.photo.hint.too_bright", "Trop de reflets, évitez la lumière directe")
336
+ : automaticCapture
337
+ ? t("id_check.photo.automatic_instruction", "Capture automatique active ou appuyez sur le bouton")
338
+ : t("id_check.photo.manual_instruction", "Appuyez sur le bouton pour capturer");
289
339
  return (jsxRuntime.jsxs("div", { className: "flex flex-col", style: {
290
340
  position: isMobile ? "fixed" : "relative",
291
341
  inset: isMobile ? 0 : "auto",
292
342
  zIndex: isMobile ? 50 : "auto",
293
343
  height: "100%",
294
344
  width: "100%",
295
- }, children: [jsxRuntime.jsx("div", { className: "flex-1 relative overflow-hidden bg-black", children: cameraError ? (jsxRuntime.jsx("div", { className: "flex items-center justify-center h-full px-4 py-6", children: jsxRuntime.jsx(CameraError.default, { errorMessage: cameraError, onRetry: handleRetry }) })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("video", { ref: videoRef, autoPlay: true, playsInline: true, muted: true, className: "absolute inset-0 w-full h-full object-cover z-0 bg-black ".concat(facingMode === "user" ? "scale-x-[-1]" : "") }), jsxRuntime.jsx("div", { className: "absolute top-4 left-1/2 transform -translate-x-1/2 z-50", children: jsxRuntime.jsx("span", { className: "text-white z-50 text-sm font-semibold drop-shadow-lg", children: automaticCapture ? (jsxRuntime.jsx("span", { className: "animate-pulse", children: t("id_check.photo.mode_autocapture", "Autocapture activée") })) : (jsxRuntime.jsx("span", { className: "bg-orange-500 px-2 py-1 rounded hidden", children: t("id_check.photo.mode_manual", "Mode manuel") })) }) }), jsxRuntime.jsx(CameraMask.default, { isDetecting: isDetecting, isPortrait: isPortrait, facingMode: facingMode, isCountingDown: isCountingDown, countdown: countdown, maskType: maskType, onMaskRectChange: setMaskRect }), jsxRuntime.jsx("div", { className: "absolute bottom-20 left-0 right-0 z-20 pointer-events-none", children: jsxRuntime.jsx("div", { className: "text-center px-4", children: jsxRuntime.jsx("div", { className: "bg-black bg-opacity-60 text-white text-sm px-4 py-2 rounded-lg inline-block", children: t("id_check.photo.position_document", "Positionnez le document") }) }) }), jsxRuntime.jsx(OrientationToggle.default, { onCameraToggle: toggleCamera })] })) }), jsxRuntime.jsx("div", { className: "relative z-20 bg-black bg-opacity-80 p-4", children: jsxRuntime.jsxs("div", { className: "flex flex-col items-center space-y-4", children: [jsxRuntime.jsx("button", { onClick: handleManualCapture, className: "w-16 h-16 rounded-full border-4 border-white flex items-center justify-center transition-all bg-transparent hover:bg-white hover:bg-opacity-20 cursor-pointer", children: jsxRuntime.jsx("div", { className: "w-8 h-8 bg-white rounded-full" }) }), jsxRuntime.jsxs("div", { className: "text-white text-center text-sm", children: [jsxRuntime.jsxs("div", { className: "flex items-center justify-center space-x-2", children: [jsxRuntime.jsx("div", { className: "w-2 h-2 rounded-full ".concat(automaticCapture
345
+ }, children: [jsxRuntime.jsx("div", { className: "flex-1 relative overflow-hidden bg-black", children: cameraError ? (jsxRuntime.jsx("div", { className: "flex items-center justify-center h-full px-4 py-6", children: jsxRuntime.jsx(CameraError.default, { errorMessage: cameraError, onRetry: handleRetry }) })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [isInitializing && (jsxRuntime.jsxs("div", { "data-testid": "camera-initializing", className: "absolute inset-0 z-30 flex flex-col items-center justify-center gap-3 bg-black", children: [jsxRuntime.jsx("div", { className: "w-10 h-10 border-4 border-white border-t-transparent rounded-full animate-spin" }), jsxRuntime.jsx("span", { className: "text-white text-sm", children: t("id_check.photo.initializing", "Ouverture de la caméra…") })] })), isProcessing && (jsxRuntime.jsxs("div", { "data-testid": "camera-processing", className: "absolute inset-0 z-30 flex flex-col items-center justify-center gap-3 bg-black bg-opacity-70", children: [jsxRuntime.jsx("div", { className: "w-10 h-10 border-4 border-white border-t-transparent rounded-full animate-spin" }), jsxRuntime.jsx("span", { className: "text-white text-sm", children: t("id_check.photo.processing", "Traitement de l'image…") })] })), jsxRuntime.jsx("video", { ref: videoRef, autoPlay: true, playsInline: true, muted: true, className: "absolute inset-0 w-full h-full object-cover z-0 bg-black ".concat(facingMode === "user" ? "scale-x-[-1]" : "") }), jsxRuntime.jsx("div", { className: "absolute top-4 left-1/2 transform -translate-x-1/2 z-50", children: jsxRuntime.jsx("span", { className: "text-white z-50 text-sm font-semibold drop-shadow-lg", children: automaticCapture ? (jsxRuntime.jsx("span", { className: "animate-pulse", children: t("id_check.photo.mode_autocapture", "Autocapture activée") })) : (jsxRuntime.jsx("span", { className: "bg-orange-500 px-2 py-1 rounded hidden", children: t("id_check.photo.mode_manual", "Mode manuel") })) }) }), jsxRuntime.jsx(CameraMask.default, { isDetecting: isDetecting, isPortrait: isPortrait, facingMode: facingMode, isCountingDown: isCountingDown, countdown: countdown, maskType: maskType, documentDetected: documentDetected, qualityHint: qualityHint, onMaskRectChange: setMaskRect }), jsxRuntime.jsx("div", { className: "absolute bottom-20 left-0 right-0 z-20 pointer-events-none", children: jsxRuntime.jsx("div", { className: "text-center px-4", children: jsxRuntime.jsx("div", { className: "bg-black bg-opacity-60 text-white text-sm px-4 py-2 rounded-lg inline-block", children: t("id_check.photo.position_document", "Posez le document à plat ou tenez-le dans le cadre") }) }) }), jsxRuntime.jsx(OrientationToggle.default, { onCameraToggle: toggleCamera }), showCaptureFlash && (jsxRuntime.jsx("div", { "data-testid": "capture-flash", className: "absolute inset-0 z-40 bg-white pointer-events-none animate-pulse" }))] })) }), jsxRuntime.jsx("div", { className: "relative z-20 bg-black bg-opacity-80 p-4", children: jsxRuntime.jsxs("div", { className: "flex flex-col items-center space-y-4", children: [jsxRuntime.jsx("button", { onClick: handleManualCapture, className: "w-16 h-16 rounded-full border-4 border-white flex items-center justify-center transition-all duration-150 bg-transparent hover:bg-white hover:bg-opacity-20 cursor-pointer ".concat(showCaptureFlash ? "scale-90 ring-4 ring-[#11E5C5]" : ""), children: jsxRuntime.jsx("div", { className: "w-8 h-8 bg-white rounded-full" }) }), jsxRuntime.jsxs("div", { className: "text-white text-center text-sm", children: [jsxRuntime.jsxs("div", { className: "flex items-center justify-center space-x-2", children: [jsxRuntime.jsx("div", { className: "w-2 h-2 rounded-full ".concat(automaticCapture
296
346
  ? "bg-[#11E5C5] animate-pulse"
297
- : "bg-orange-500") }), jsxRuntime.jsx("span", { children: automaticCapture
298
- ? t("id_check.photo.automatic_instruction", "Capture automatique active ou appuyez sur le bouton")
299
- : t("id_check.photo.manual_instruction", "Appuyez sur le bouton pour capturer") })] }), captureError && (jsxRuntime.jsx("p", { className: "text-red-400 text-xs mt-2 text-center", children: captureError }))] })] }) })] }));
347
+ : "bg-orange-500") }), jsxRuntime.jsx("span", { children: statusMessage })] }), captureError && (jsxRuntime.jsx("p", { className: "text-red-400 text-xs mt-2 text-center", children: captureError }))] })] }) })] }));
300
348
  };
301
349
 
302
350
  exports.default = Photo;