datakeen-session-react 1.1.140-rc.80 → 1.1.140-rc.82
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/components/nfc-scan/NfcFallbackSurvey.js +6 -2
- package/dist/cjs/components/nfc-scan/NfcFallbackSurvey.js.map +1 -1
- package/dist/cjs/components/nfc-scan/NfcScanNode.js +44 -4
- package/dist/cjs/components/nfc-scan/NfcScanNode.js.map +1 -1
- package/dist/cjs/components/session/DocumentCheck.js +72 -12
- package/dist/cjs/components/session/DocumentCheck.js.map +1 -1
- package/dist/cjs/hooks/useNfcSseStatus.js +8 -9
- package/dist/cjs/hooks/useNfcSseStatus.js.map +1 -1
- package/dist/cjs/i18n/en.json.js +6 -0
- package/dist/cjs/i18n/en.json.js.map +1 -1
- package/dist/cjs/i18n/fr.json.js +6 -0
- package/dist/cjs/i18n/fr.json.js.map +1 -1
- package/dist/cjs/index.css.js +1 -1
- package/dist/cjs/services/sessionService.js +16 -0
- package/dist/cjs/services/sessionService.js.map +1 -1
- package/dist/esm/components/nfc-scan/NfcFallbackSurvey.js +6 -2
- package/dist/esm/components/nfc-scan/NfcFallbackSurvey.js.map +1 -1
- package/dist/esm/components/nfc-scan/NfcScanNode.js +45 -5
- package/dist/esm/components/nfc-scan/NfcScanNode.js.map +1 -1
- package/dist/esm/components/session/DocumentCheck.js +72 -12
- package/dist/esm/components/session/DocumentCheck.js.map +1 -1
- package/dist/esm/hooks/useNfcSseStatus.js +8 -9
- package/dist/esm/hooks/useNfcSseStatus.js.map +1 -1
- package/dist/esm/i18n/en.json.js +6 -0
- package/dist/esm/i18n/en.json.js.map +1 -1
- package/dist/esm/i18n/fr.json.js +6 -0
- package/dist/esm/i18n/fr.json.js.map +1 -1
- package/dist/esm/index.css.js +1 -1
- package/dist/esm/services/sessionService.js +16 -1
- package/dist/esm/services/sessionService.js.map +1 -1
- package/package.json +1 -1
|
@@ -12,6 +12,7 @@ var MobilePageLayout = require('../ui/MobilePageLayout.js');
|
|
|
12
12
|
var useI18n = require('../../hooks/useI18n.js');
|
|
13
13
|
|
|
14
14
|
var REASON_KEYS = [
|
|
15
|
+
"reason_scan_failed",
|
|
15
16
|
"reason_no_id",
|
|
16
17
|
"reason_no_nfc",
|
|
17
18
|
"reason_unknown_nfc",
|
|
@@ -19,12 +20,15 @@ var REASON_KEYS = [
|
|
|
19
20
|
"reason_other",
|
|
20
21
|
];
|
|
21
22
|
var REASON_FALLBACKS = {
|
|
23
|
+
reason_scan_failed: "Le scan n'a pas fonctionné",
|
|
22
24
|
reason_no_id: "Je n'ai pas ma pièce d'identité avec moi",
|
|
23
25
|
reason_no_nfc: "Ma pièce d'identité n'a pas de puce",
|
|
24
26
|
reason_unknown_nfc: "Je ne sais pas si ma pièce a une puce",
|
|
25
27
|
reason_not_comfortable: "Je ne souhaite pas utiliser le scan sans contact",
|
|
26
28
|
reason_other: "Autre raison",
|
|
27
29
|
};
|
|
30
|
+
// Raisons pour lesquelles on propose un commentaire libre optionnel ("nous en dire plus").
|
|
31
|
+
var REASONS_WITH_COMMENT = ["reason_scan_failed", "reason_other"];
|
|
28
32
|
/**
|
|
29
33
|
* Questionnaire de fallback affiché lorsqu'un utilisateur déclare ne pas pouvoir
|
|
30
34
|
* utiliser le scan NFC (écran NfcChipGate → "Non"). À la manière de Persona, on
|
|
@@ -43,7 +47,7 @@ var NfcFallbackSurvey = function (_a) {
|
|
|
43
47
|
return;
|
|
44
48
|
var label = t("nfc_scan.fallback.".concat(selectedReason), REASON_FALLBACKS[selectedReason]);
|
|
45
49
|
var trimmedComment = comment.trim();
|
|
46
|
-
onSubmit(selectedReason, label, selectedReason
|
|
50
|
+
onSubmit(selectedReason, label, REASONS_WITH_COMMENT.includes(selectedReason) && trimmedComment
|
|
47
51
|
? trimmedComment
|
|
48
52
|
: undefined);
|
|
49
53
|
};
|
|
@@ -63,7 +67,7 @@ var NfcFallbackSurvey = function (_a) {
|
|
|
63
67
|
}, children: isSelected && (jsxRuntime.jsx("span", { className: "h-2.5 w-2.5 rounded-full", style: {
|
|
64
68
|
backgroundColor: "var(--uni-primary-color, #11e5c5)",
|
|
65
69
|
} })) }), jsxRuntime.jsx("span", { className: "text-sm font-medium text-gray-900", children: t("nfc_scan.fallback.".concat(reasonKey), REASON_FALLBACKS[reasonKey]) })] }, reasonKey));
|
|
66
|
-
}) }), selectedReason
|
|
70
|
+
}) }), selectedReason && REASONS_WITH_COMMENT.includes(selectedReason) && (jsxRuntime.jsx("textarea", { value: comment, onChange: function (e) { return setComment(e.target.value); }, maxLength: 1000, rows: 3, placeholder: t("nfc_scan.fallback.comment_placeholder", "Précisez votre raison (optionnel)"), className: "w-full resize-none rounded-xl border-2 border-gray-200 p-3 text-sm text-gray-900 focus:outline-none" }))] }) }) }));
|
|
67
71
|
};
|
|
68
72
|
|
|
69
73
|
exports.default = NfcFallbackSurvey;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NfcFallbackSurvey.js","sources":["../../../../../src/components/nfc-scan/NfcFallbackSurvey.tsx"],"sourcesContent":["import React, { useState } from \"react\";\nimport Title from \"../ui/Title\";\nimport Subtitle from \"../ui/Subtitle\";\nimport Button from \"../ui/Button\";\nimport PageActions from \"../ui/PageActions\";\nimport MobilePageLayout from \"../ui/MobilePageLayout\";\nimport { useI18n } from \"../../hooks/useI18n\";\n\ninterface NfcFallbackSurveyProps {\n /**\n * Validation du questionnaire.\n * @param reason clé technique de la raison sélectionnée\n * @param label libellé lisible (traduit) — stocké tel quel côté backoffice\n * @param comment commentaire libre optionnel (uniquement si \"autre\")\n */\n onSubmit: (reason: string, label: string, comment?: string) => void;\n}\n\nconst REASON_KEYS = [\n \"reason_no_id\",\n \"reason_no_nfc\",\n \"reason_unknown_nfc\",\n \"reason_not_comfortable\",\n \"reason_other\",\n] as const;\n\ntype ReasonKey = (typeof REASON_KEYS)[number];\n\nconst REASON_FALLBACKS: Record<ReasonKey, string> = {\n reason_no_id: \"Je n'ai pas ma pièce d'identité avec moi\",\n reason_no_nfc: \"Ma pièce d'identité n'a pas de puce\",\n reason_unknown_nfc: \"Je ne sais pas si ma pièce a une puce\",\n reason_not_comfortable: \"Je ne souhaite pas utiliser le scan sans contact\",\n reason_other: \"Autre raison\",\n};\n\n/**\n * Questionnaire de fallback affiché lorsqu'un utilisateur déclare ne pas pouvoir\n * utiliser le scan NFC (écran NfcChipGate → \"Non\"). À la manière de Persona, on\n * recueille la raison pour aider les reviewers côté backoffice avant de poursuivre.\n *\n * La sélection d'une raison est obligatoire ; un commentaire libre optionnel\n * apparaît lorsque \"Autre raison\" est choisi.\n */\nconst NfcFallbackSurvey = ({ onSubmit }: NfcFallbackSurveyProps) => {\n const { t } = useI18n();\n const [selectedReason, setSelectedReason] = useState<ReasonKey | null>(null);\n const [comment, setComment] = useState(\"\");\n\n const handleSubmit = () => {\n if (!selectedReason) return;\n const label = t(\n `nfc_scan.fallback.${selectedReason}`,\n REASON_FALLBACKS[selectedReason],\n );\n const trimmedComment = comment.trim();\n onSubmit(\n selectedReason,\n label,\n selectedReason
|
|
1
|
+
{"version":3,"file":"NfcFallbackSurvey.js","sources":["../../../../../src/components/nfc-scan/NfcFallbackSurvey.tsx"],"sourcesContent":["import React, { useState } from \"react\";\nimport Title from \"../ui/Title\";\nimport Subtitle from \"../ui/Subtitle\";\nimport Button from \"../ui/Button\";\nimport PageActions from \"../ui/PageActions\";\nimport MobilePageLayout from \"../ui/MobilePageLayout\";\nimport { useI18n } from \"../../hooks/useI18n\";\n\ninterface NfcFallbackSurveyProps {\n /**\n * Validation du questionnaire.\n * @param reason clé technique de la raison sélectionnée\n * @param label libellé lisible (traduit) — stocké tel quel côté backoffice\n * @param comment commentaire libre optionnel (uniquement si \"autre\")\n */\n onSubmit: (reason: string, label: string, comment?: string) => void;\n}\n\nconst REASON_KEYS = [\n \"reason_scan_failed\",\n \"reason_no_id\",\n \"reason_no_nfc\",\n \"reason_unknown_nfc\",\n \"reason_not_comfortable\",\n \"reason_other\",\n] as const;\n\ntype ReasonKey = (typeof REASON_KEYS)[number];\n\nconst REASON_FALLBACKS: Record<ReasonKey, string> = {\n reason_scan_failed: \"Le scan n'a pas fonctionné\",\n reason_no_id: \"Je n'ai pas ma pièce d'identité avec moi\",\n reason_no_nfc: \"Ma pièce d'identité n'a pas de puce\",\n reason_unknown_nfc: \"Je ne sais pas si ma pièce a une puce\",\n reason_not_comfortable: \"Je ne souhaite pas utiliser le scan sans contact\",\n reason_other: \"Autre raison\",\n};\n\n// Raisons pour lesquelles on propose un commentaire libre optionnel (\"nous en dire plus\").\nconst REASONS_WITH_COMMENT: ReasonKey[] = [\"reason_scan_failed\", \"reason_other\"];\n\n/**\n * Questionnaire de fallback affiché lorsqu'un utilisateur déclare ne pas pouvoir\n * utiliser le scan NFC (écran NfcChipGate → \"Non\"). À la manière de Persona, on\n * recueille la raison pour aider les reviewers côté backoffice avant de poursuivre.\n *\n * La sélection d'une raison est obligatoire ; un commentaire libre optionnel\n * apparaît lorsque \"Autre raison\" est choisi.\n */\nconst NfcFallbackSurvey = ({ onSubmit }: NfcFallbackSurveyProps) => {\n const { t } = useI18n();\n const [selectedReason, setSelectedReason] = useState<ReasonKey | null>(null);\n const [comment, setComment] = useState(\"\");\n\n const handleSubmit = () => {\n if (!selectedReason) return;\n const label = t(\n `nfc_scan.fallback.${selectedReason}`,\n REASON_FALLBACKS[selectedReason],\n );\n const trimmedComment = comment.trim();\n onSubmit(\n selectedReason,\n label,\n REASONS_WITH_COMMENT.includes(selectedReason) && trimmedComment\n ? trimmedComment\n : undefined,\n );\n };\n\n return (\n <MobilePageLayout\n footer={\n <PageActions\n primary={\n <Button onClick={handleSubmit} disabled={!selectedReason}>\n {t(\"nfc_scan.fallback.continue\", \"Continuer\")}\n </Button>\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 <div className=\"text-center space-y-4\">\n <Title className=\"text-xl md:text-2xl lg:text-3xl\">\n {t(\"nfc_scan.fallback.title\", \"Aidez-nous à comprendre\")}\n </Title>\n <Subtitle className=\"text-sm text-gray-600 leading-relaxed whitespace-pre-line\">\n {t(\n \"nfc_scan.fallback.subtitle\",\n \"Pour quelle raison ne pouvez-vous pas utiliser le scan sans contact ?\",\n )}\n </Subtitle>\n </div>\n\n <div className=\"space-y-3\">\n {REASON_KEYS.map((reasonKey) => {\n const isSelected = selectedReason === reasonKey;\n return (\n <button\n key={reasonKey}\n type=\"button\"\n onClick={() => setSelectedReason(reasonKey)}\n aria-pressed={isSelected}\n className=\"flex w-full items-center gap-3 rounded-xl border-2 p-4 text-left transition-colors\"\n style={{\n borderColor: isSelected\n ? \"var(--uni-primary-color, #11e5c5)\"\n : \"#e5e7eb\",\n backgroundColor: isSelected\n ? \"var(--uni-light-color-variant-1, #d0f7f2)\"\n : \"#ffffff\",\n }}\n >\n <span\n className=\"flex h-5 w-5 shrink-0 items-center justify-center rounded-full border-2\"\n style={{\n borderColor: isSelected\n ? \"var(--uni-primary-color, #11e5c5)\"\n : \"#d1d5db\",\n }}\n >\n {isSelected && (\n <span\n className=\"h-2.5 w-2.5 rounded-full\"\n style={{\n backgroundColor: \"var(--uni-primary-color, #11e5c5)\",\n }}\n />\n )}\n </span>\n <span className=\"text-sm font-medium text-gray-900\">\n {t(\n `nfc_scan.fallback.${reasonKey}`,\n REASON_FALLBACKS[reasonKey],\n )}\n </span>\n </button>\n );\n })}\n </div>\n\n {selectedReason && REASONS_WITH_COMMENT.includes(selectedReason) && (\n <textarea\n value={comment}\n onChange={(e) => setComment(e.target.value)}\n maxLength={1000}\n rows={3}\n placeholder={t(\n \"nfc_scan.fallback.comment_placeholder\",\n \"Précisez votre raison (optionnel)\",\n )}\n className=\"w-full resize-none rounded-xl border-2 border-gray-200 p-3 text-sm text-gray-900 focus:outline-none\"\n />\n )}\n </div>\n </div>\n </MobilePageLayout>\n );\n};\n\nexport default NfcFallbackSurvey;\n"],"names":["useI18n","useState","_jsx","MobilePageLayout","PageActions","Button","_jsxs","Title","Subtitle"],"mappings":"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"}
|
|
@@ -9,18 +9,22 @@ var QRCode = require('qrcode');
|
|
|
9
9
|
var useI18n = require('../../hooks/useI18n.js');
|
|
10
10
|
var useNfcSseStatus = require('../../hooks/useNfcSseStatus.js');
|
|
11
11
|
var api = require('../../services/api.js');
|
|
12
|
+
var sessionService = require('../../services/sessionService.js');
|
|
12
13
|
var isMobileDevice = require('../../utils/isMobileDevice.js');
|
|
13
14
|
var MobilePageLayout = require('../ui/MobilePageLayout.js');
|
|
14
15
|
var Button = require('../ui/Button.js');
|
|
16
|
+
var PageActions = require('../ui/PageActions.js');
|
|
15
17
|
var Title = require('../ui/Title.js');
|
|
16
18
|
var Subtitle = require('../ui/Subtitle.js');
|
|
17
19
|
var NfcSuccessAnimation = require('./NfcSuccessAnimation.js');
|
|
18
20
|
|
|
19
21
|
var NfcScanStep = function (_a) {
|
|
20
|
-
var sessionId = _a.sessionId, nodeId = _a.nodeId, stepObject = _a.stepObject, template = _a.template, pageTitle = _a.pageTitle, pageDescription = _a.pageDescription, onBack = _a.onBack;
|
|
22
|
+
var sessionId = _a.sessionId, nodeId = _a.nodeId, stepObject = _a.stepObject, template = _a.template, pageTitle = _a.pageTitle, pageDescription = _a.pageDescription, onBack = _a.onBack, onContinueWithoutChip = _a.onContinueWithoutChip;
|
|
21
23
|
var t = useI18n.useI18n().t;
|
|
22
24
|
var canvasRef = React.useRef(null);
|
|
23
25
|
var _b = React.useState(null), qrError = _b[0], setQrError = _b[1];
|
|
26
|
+
var _c = React.useState(null), resetError = _c[0], setResetError = _c[1];
|
|
27
|
+
var _d = React.useState(false), isResetting = _d[0], setIsResetting = _d[1];
|
|
24
28
|
// Sur mobile, on ouvre directement l'app de scan au lieu d'afficher un QR code inutile.
|
|
25
29
|
var isMobile = React.useMemo(function () { return isMobileDevice.isMobileDevice(); }, []);
|
|
26
30
|
var nfcUrl = React.useMemo(function () {
|
|
@@ -98,10 +102,46 @@ var NfcScanStep = function (_a) {
|
|
|
98
102
|
}); };
|
|
99
103
|
generate();
|
|
100
104
|
}, [nfcUrl, status, t, isMobile]);
|
|
101
|
-
|
|
105
|
+
var handleBack = function () { return tslib_es6.__awaiter(void 0, void 0, void 0, function () {
|
|
106
|
+
return tslib_es6.__generator(this, function (_b) {
|
|
107
|
+
switch (_b.label) {
|
|
108
|
+
case 0:
|
|
109
|
+
if (!onBack)
|
|
110
|
+
return [2 /*return*/];
|
|
111
|
+
if (status !== "failed") {
|
|
112
|
+
onBack();
|
|
113
|
+
return [2 /*return*/];
|
|
114
|
+
}
|
|
115
|
+
setResetError(null);
|
|
116
|
+
setIsResetting(true);
|
|
117
|
+
_b.label = 1;
|
|
118
|
+
case 1:
|
|
119
|
+
_b.trys.push([1, 3, 4, 5]);
|
|
120
|
+
return [4 /*yield*/, sessionService.resetNfcScan(sessionId)];
|
|
121
|
+
case 2:
|
|
122
|
+
_b.sent();
|
|
123
|
+
onBack();
|
|
124
|
+
return [3 /*break*/, 5];
|
|
125
|
+
case 3:
|
|
126
|
+
_b.sent();
|
|
127
|
+
setResetError(t("nfc_scan.reset_failed", "Impossible de réinitialiser le scan NFC. Veuillez réessayer."));
|
|
128
|
+
return [3 /*break*/, 5];
|
|
129
|
+
case 4:
|
|
130
|
+
setIsResetting(false);
|
|
131
|
+
return [7 /*endfinally*/];
|
|
132
|
+
case 5: return [2 /*return*/];
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
}); };
|
|
136
|
+
// Footer : sur l'échec définitif ('failed'), les actions dédiées (revenir au choix / poursuivre
|
|
137
|
+
// sans puce) sont rendues via PageActions, comme les autres écrans NFC (NfcChipGate). Sinon, un
|
|
138
|
+
// simple "Retour" tant que le scan n'a pas commencé/abouti.
|
|
102
139
|
var canGoBack = !!onBack && (status === "pending" || status === "opened");
|
|
103
|
-
var footer = canGoBack ? (jsxRuntime.jsx(Button.default, { variant: "secondary", onClick: onBack, children: t("common.back", "Retour") })) : null;
|
|
104
|
-
return (jsxRuntime.jsx(MobilePageLayout.default, { footer: footer, 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 mt-16 md:mt-20", children: [jsxRuntime.jsx(Title.default, { className: "text-xl md:text-2xl lg:text-3xl", children: title }), jsxRuntime.jsx(Subtitle.default, { className: "text-sm md:text-base text-gray-600 leading-relaxed whitespace-pre-line", children: subtitle })] }), jsxRuntime.jsx("div", { className: "flex-1 flex justify-center items-center", children: status === "success" ? (jsxRuntime.jsx(NfcSuccessAnimation.default, {})) : status === "completed" ? (jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-4 p-8", children: [jsxRuntime.jsx("div", { className: "animate-spin w-10 h-10 border-2 border-gray-300 border-t-blue-500 rounded-full" }), jsxRuntime.jsx("p", { className: "text-sm text-gray-600", children: t("nfc_scan.verifying", "Vérification en cours…") })] })) : status === "
|
|
140
|
+
var footer = status === "failed" ? (jsxRuntime.jsx(PageActions.default, { primary: onBack ? (jsxRuntime.jsx(Button.default, { onClick: function () { return void handleBack(); }, disabled: isResetting, children: t("nfc_scan.failed_back_to_selection", "Revenir au choix du document") })) : null, secondary: onContinueWithoutChip ? (jsxRuntime.jsx(Button.default, { variant: "secondary", onClick: onContinueWithoutChip, children: t("nfc_scan.failed_continue_without_chip", "Poursuivre sans puce") })) : null })) : canGoBack ? (jsxRuntime.jsx(Button.default, { variant: "secondary", onClick: onBack, children: t("common.back", "Retour") })) : null;
|
|
141
|
+
return (jsxRuntime.jsx(MobilePageLayout.default, { footer: footer, 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 mt-16 md:mt-20", children: [jsxRuntime.jsx(Title.default, { className: "text-xl md:text-2xl lg:text-3xl", children: title }), jsxRuntime.jsx(Subtitle.default, { className: "text-sm md:text-base text-gray-600 leading-relaxed whitespace-pre-line", children: subtitle })] }), jsxRuntime.jsx("div", { className: "flex-1 flex flex-col justify-center items-center gap-4", children: status === "success" ? (jsxRuntime.jsx(NfcSuccessAnimation.default, {})) : status === "completed" ? (jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-4 p-8", children: [jsxRuntime.jsx("div", { className: "animate-spin w-10 h-10 border-2 border-gray-300 border-t-blue-500 rounded-full" }), jsxRuntime.jsx("p", { className: "text-sm text-gray-600", children: t("nfc_scan.verifying", "Vérification en cours…") })] })) : status === "failed" ? (jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-2 p-8 text-center", children: [jsxRuntime.jsx("p", { className: "text-base font-semibold text-red-600", children: t("nfc_scan.failed_title", "Le scan n'a pas pu être vérifié") }), jsxRuntime.jsx("p", { className: "text-sm text-gray-600 leading-relaxed", children: t("nfc_scan.failed_description", "Après plusieurs tentatives, la lecture de votre document n'a pas pu être validée.") }), resetError && (jsxRuntime.jsx("p", { className: "text-sm text-red-600", role: "alert", children: resetError }))] })) : (
|
|
142
|
+
// Contenu principal QR/mobile — reste monté pour "pending" ET "opened", pour ne
|
|
143
|
+
// jamais démonter le canvas déjà généré.
|
|
144
|
+
jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [qrError ? (jsxRuntime.jsx("div", { className: "text-red-500 text-center p-4", children: qrError })) : isMobile ? (jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-4 p-8 text-center", children: [jsxRuntime.jsx("div", { className: "animate-spin w-10 h-10 border-2 border-gray-300 border-t-blue-500 rounded-full" }), jsxRuntime.jsx("p", { className: "text-sm text-gray-600", children: t("nfc_scan.opening_app", "Ouverture de l'application…") }), jsxRuntime.jsx("a", { href: nfcUrl, className: "text-sm font-medium text-blue-600 underline", children: t("nfc_scan.open_app_link", "Ouvrir l'application") })] })) : (jsxRuntime.jsx("div", { className: "bg-white p-4 rounded-lg shadow-lg border-2 border-gray-200", children: jsxRuntime.jsx("canvas", { ref: canvasRef }) })), status === "opened" && (jsxRuntime.jsxs("div", { className: "text-center space-y-1", children: [jsxRuntime.jsx("p", { className: "text-sm font-semibold", style: { color: "var(--uni-secondary-color, #0a9983)" }, children: t("nfc_scan.qr_opened_title", "QR code scanné") }), jsxRuntime.jsx("p", { className: "text-xs text-gray-500 leading-relaxed", children: t("nfc_scan.qr_opened_description", "L'expérience NFC est ouverte sur votre smartphone. Continuez le scan sur votre téléphone.") }), jsxRuntime.jsxs("div", { className: "flex items-center justify-center gap-2 text-xs text-gray-500 pt-1", children: [jsxRuntime.jsx("span", { className: "h-2 w-2 rounded-full animate-pulse", style: { backgroundColor: "var(--uni-primary-color, #11e5c5)" } }), t("nfc_scan.waiting_completion", "En attente de la lecture NFC")] })] }))] })) })] }) }) }));
|
|
105
145
|
};
|
|
106
146
|
|
|
107
147
|
exports.default = NfcScanStep;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NfcScanNode.js","sources":["../../../../../src/components/nfc-scan/NfcScanNode.tsx"],"sourcesContent":["import React, { useEffect, useMemo, useRef, useState } from \"react\";\nimport QRCode from \"qrcode\";\nimport type { SessionTemplate, stepObject } from \"../../types/session\";\nimport { useI18n } from \"../../hooks/useI18n\";\nimport { useNfcSseStatus } from \"../../hooks/useNfcSseStatus\";\nimport { getBaseURL } from \"../../services/api\";\nimport { isMobileDevice } from \"../../utils/isMobileDevice\";\nimport MobilePageLayout from \"../ui/MobilePageLayout\";\nimport Button from \"../ui/Button\";\nimport Title from \"../ui/Title\";\nimport Subtitle from \"../ui/Subtitle\";\nimport NfcSuccessAnimation from \"./NfcSuccessAnimation\";\n\ninterface NfcScanStepProps {\n sessionId: string;\n nodeId: string;\n stepObject: stepObject;\n template?: SessionTemplate;\n pageTitle?: string;\n pageDescription?: string;\n /** Retour vers la sélection du document (pour corriger un mauvais choix). */\n onBack?: () => void;\n}\n\nconst NfcScanStep: React.FC<NfcScanStepProps> = ({\n sessionId,\n nodeId,\n stepObject,\n template,\n pageTitle,\n pageDescription,\n onBack,\n}) => {\n const { t } = useI18n();\n const canvasRef = useRef<HTMLCanvasElement>(null);\n const [qrError, setQrError] = useState<string | null>(null);\n // Sur mobile, on ouvre directement l'app de scan au lieu d'afficher un QR code inutile.\n const isMobile = useMemo(() => isMobileDevice(), []);\n\n const nfcUrl = useMemo(() => {\n const origin = typeof window !== \"undefined\" ? window.location.origin : \"\";\n return `${origin}/scan/${sessionId}`;\n }, [sessionId]);\n\n const apiBaseUrl = useMemo(() => getBaseURL(), []);\n\n const { status } = useNfcSseStatus({\n sessionId,\n apiBaseUrl,\n onCompleted: () => {\n if (template) {\n stepObject.goToNextStep(nodeId, template);\n } else {\n stepObject.setStep(stepObject.step + 1);\n }\n },\n });\n\n const title =\n pageTitle || t(\"nfc_scan.page_title\", \"Scan NFC de votre document\");\n const subtitle =\n pageDescription ||\n t(\n \"nfc_scan.page_description\",\n \"Scannez le QR code ci-dessous avec votre smartphone pour lire la puce électronique de votre document d'identité.\"\n );\n\n // Sur mobile : ouvrir directement l'application de scan (pas de QR à se scanner soi-même).\n // Une seule fois, tant que le scan n'a pas progressé. Le SSE reste actif pour faire avancer\n // le parcours quand la lecture est terminée.\n const hasRedirectedRef = useRef(false);\n useEffect(() => {\n if (!isMobile || status !== \"pending\" || hasRedirectedRef.current) return;\n hasRedirectedRef.current = true;\n window.location.assign(nfcUrl);\n }, [isMobile, status, nfcUrl]);\n\n useEffect(() => {\n if (isMobile) return; // pas de QR sur mobile (redirection directe)\n if (status !== \"pending\") return;\n\n const generate = async () => {\n if (!canvasRef.current) return;\n setQrError(null);\n\n try {\n const dataUrl = await QRCode.toDataURL(nfcUrl, {\n width: 256,\n margin: 2,\n color: { dark: \"#000000\", light: \"#FFFFFF\" },\n });\n const canvas = canvasRef.current;\n const ctx = canvas.getContext(\"2d\");\n if (ctx) {\n const img = new Image();\n img.onload = () => {\n canvas.width = img.width;\n canvas.height = img.height;\n ctx.drawImage(img, 0, 0);\n };\n img.src = dataUrl;\n }\n } catch {\n setQrError(t(\"qr_code.generation_failed\", \"Erreur de génération du QR code\"));\n }\n };\n\n generate();\n }, [nfcUrl, status, t, isMobile]);\n\n // Bouton retour : seulement tant que le scan n'a pas commencé/abouti, et si un handler est fourni.\n const canGoBack = !!onBack && (status === \"pending\" || status === \"opened\");\n const footer = canGoBack ? (\n <Button variant=\"secondary\" onClick={onBack}>\n {t(\"common.back\", \"Retour\")}\n </Button>\n ) : null;\n\n return (\n <MobilePageLayout footer={footer}>\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 <div className=\"text-center space-y-4 mt-16 md:mt-20\">\n <Title className=\"text-xl md:text-2xl lg:text-3xl\">{title}</Title>\n <Subtitle className=\"text-sm md:text-base text-gray-600 leading-relaxed whitespace-pre-line\">\n {subtitle}\n </Subtitle>\n </div>\n\n <div className=\"flex-1 flex justify-center items-center\">\n {status === \"success\" ? (\n <NfcSuccessAnimation />\n ) : status === \"completed\" ? (\n <div className=\"flex flex-col items-center gap-4 p-8\">\n <div className=\"animate-spin w-10 h-10 border-2 border-gray-300 border-t-blue-500 rounded-full\" />\n <p className=\"text-sm text-gray-600\">\n {t(\"nfc_scan.verifying\", \"Vérification en cours…\")}\n </p>\n </div>\n ) : status === \"opened\" ? (\n <div className=\"w-full max-w-sm rounded-2xl border border-emerald-200 bg-emerald-50 p-6 text-center shadow-sm\">\n <div className=\"mx-auto mb-4 flex h-14 w-14 items-center justify-center rounded-full bg-emerald-500 text-white shadow-md shadow-emerald-200\">\n <svg\n aria-hidden=\"true\"\n viewBox=\"0 0 24 24\"\n className=\"h-7 w-7\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"2.5\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n >\n <path d=\"M20 6 9 17l-5-5\" />\n </svg>\n </div>\n <p className=\"text-base font-semibold text-gray-900\">\n {t(\"nfc_scan.qr_opened_title\", \"QR code scanné\")}\n </p>\n <p className=\"mt-2 text-sm leading-relaxed text-gray-600\">\n {t(\n \"nfc_scan.qr_opened_description\",\n \"L'expérience NFC est ouverte sur votre smartphone. Continuez le scan sur votre téléphone.\"\n )}\n </p>\n <div className=\"mt-5 flex items-center justify-center gap-2 text-xs font-medium text-emerald-700\">\n <span className=\"h-2 w-2 animate-pulse rounded-full bg-emerald-500\" />\n {t(\"nfc_scan.waiting_completion\", \"En attente de la lecture NFC\")}\n </div>\n </div>\n ) : qrError ? (\n <div className=\"text-red-500 text-center p-4\">{qrError}</div>\n ) : isMobile ? (\n <div className=\"flex flex-col items-center gap-4 p-8 text-center\">\n <div className=\"animate-spin w-10 h-10 border-2 border-gray-300 border-t-blue-500 rounded-full\" />\n <p className=\"text-sm text-gray-600\">\n {t(\"nfc_scan.opening_app\", \"Ouverture de l'application…\")}\n </p>\n <a\n href={nfcUrl}\n className=\"text-sm font-medium text-blue-600 underline\"\n >\n {t(\"nfc_scan.open_app_link\", \"Ouvrir l'application\")}\n </a>\n </div>\n ) : (\n <div className=\"bg-white p-4 rounded-lg shadow-lg border-2 border-gray-200\">\n <canvas ref={canvasRef} />\n </div>\n )}\n </div>\n </div>\n </div>\n </MobilePageLayout>\n );\n};\n\nexport default NfcScanStep;\n"],"names":["useI18n","useRef","useState","useMemo","isMobileDevice","getBaseURL","useNfcSseStatus","useEffect","__awaiter","_jsx","Button","MobilePageLayout","_jsxs","Title","Subtitle","NfcSuccessAnimation"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"NfcScanNode.js","sources":["../../../../../src/components/nfc-scan/NfcScanNode.tsx"],"sourcesContent":["import React, { useEffect, useMemo, useRef, useState } from \"react\";\nimport QRCode from \"qrcode\";\nimport type { SessionTemplate, stepObject } from \"../../types/session\";\nimport { useI18n } from \"../../hooks/useI18n\";\nimport { useNfcSseStatus } from \"../../hooks/useNfcSseStatus\";\nimport { getBaseURL } from \"../../services/api\";\nimport { resetNfcScan } from \"../../services/sessionService\";\nimport { isMobileDevice } from \"../../utils/isMobileDevice\";\nimport MobilePageLayout from \"../ui/MobilePageLayout\";\nimport Button from \"../ui/Button\";\nimport PageActions from \"../ui/PageActions\";\nimport Title from \"../ui/Title\";\nimport Subtitle from \"../ui/Subtitle\";\nimport NfcSuccessAnimation from \"./NfcSuccessAnimation\";\n\ninterface NfcScanStepProps {\n sessionId: string;\n nodeId: string;\n stepObject: stepObject;\n template?: SessionTemplate;\n pageTitle?: string;\n pageDescription?: string;\n /** Retour vers la sélection du document (pour corriger un mauvais choix). */\n onBack?: () => void;\n /**\n * Fallback \"poursuivre sans puce\" proposé sur l'échec définitif (nfcStatus 'failed'). Ouvre le\n * questionnaire de raison côté web (NfcFallbackSurvey) ; la reprise (photo/API ou saut de nœud)\n * est décidée par l'orchestrateur selon nfcMode. Absent = bouton masqué.\n */\n onContinueWithoutChip?: () => void;\n}\n\nconst NfcScanStep: React.FC<NfcScanStepProps> = ({\n sessionId,\n nodeId,\n stepObject,\n template,\n pageTitle,\n pageDescription,\n onBack,\n onContinueWithoutChip,\n}) => {\n const { t } = useI18n();\n const canvasRef = useRef<HTMLCanvasElement>(null);\n const [qrError, setQrError] = useState<string | null>(null);\n const [resetError, setResetError] = useState<string | null>(null);\n const [isResetting, setIsResetting] = useState(false);\n // Sur mobile, on ouvre directement l'app de scan au lieu d'afficher un QR code inutile.\n const isMobile = useMemo(() => isMobileDevice(), []);\n\n const nfcUrl = useMemo(() => {\n const origin = typeof window !== \"undefined\" ? window.location.origin : \"\";\n return `${origin}/scan/${sessionId}`;\n }, [sessionId]);\n\n const apiBaseUrl = useMemo(() => getBaseURL(), []);\n\n const { status } = useNfcSseStatus({\n sessionId,\n apiBaseUrl,\n onCompleted: () => {\n if (template) {\n stepObject.goToNextStep(nodeId, template);\n } else {\n stepObject.setStep(stepObject.step + 1);\n }\n },\n });\n\n const title =\n pageTitle || t(\"nfc_scan.page_title\", \"Scan NFC de votre document\");\n const subtitle =\n pageDescription ||\n t(\n \"nfc_scan.page_description\",\n \"Scannez le QR code ci-dessous avec votre smartphone pour lire la puce électronique de votre document d'identité.\"\n );\n\n // Sur mobile : ouvrir directement l'application de scan (pas de QR à se scanner soi-même).\n // Une seule fois, tant que le scan n'a pas progressé. Le SSE reste actif pour faire avancer\n // le parcours quand la lecture est terminée.\n const hasRedirectedRef = useRef(false);\n useEffect(() => {\n if (!isMobile || status !== \"pending\" || hasRedirectedRef.current) return;\n hasRedirectedRef.current = true;\n window.location.assign(nfcUrl);\n }, [isMobile, status, nfcUrl]);\n\n useEffect(() => {\n if (isMobile) return; // pas de QR sur mobile (redirection directe)\n if (status !== \"pending\") return;\n\n const generate = async () => {\n if (!canvasRef.current) return;\n setQrError(null);\n\n try {\n const dataUrl = await QRCode.toDataURL(nfcUrl, {\n width: 256,\n margin: 2,\n color: { dark: \"#000000\", light: \"#FFFFFF\" },\n });\n const canvas = canvasRef.current;\n const ctx = canvas.getContext(\"2d\");\n if (ctx) {\n const img = new Image();\n img.onload = () => {\n canvas.width = img.width;\n canvas.height = img.height;\n ctx.drawImage(img, 0, 0);\n };\n img.src = dataUrl;\n }\n } catch {\n setQrError(t(\"qr_code.generation_failed\", \"Erreur de génération du QR code\"));\n }\n };\n\n generate();\n }, [nfcUrl, status, t, isMobile]);\n\n const handleBack = async () => {\n if (!onBack) return;\n\n if (status !== \"failed\") {\n onBack();\n return;\n }\n\n setResetError(null);\n setIsResetting(true);\n try {\n await resetNfcScan(sessionId);\n onBack();\n } catch {\n setResetError(\n t(\n \"nfc_scan.reset_failed\",\n \"Impossible de réinitialiser le scan NFC. Veuillez réessayer.\",\n ),\n );\n } finally {\n setIsResetting(false);\n }\n };\n\n // Footer : sur l'échec définitif ('failed'), les actions dédiées (revenir au choix / poursuivre\n // sans puce) sont rendues via PageActions, comme les autres écrans NFC (NfcChipGate). Sinon, un\n // simple \"Retour\" tant que le scan n'a pas commencé/abouti.\n const canGoBack = !!onBack && (status === \"pending\" || status === \"opened\");\n const footer =\n status === \"failed\" ? (\n <PageActions\n primary={\n onBack ? (\n <Button onClick={() => void handleBack()} disabled={isResetting}>\n {t(\"nfc_scan.failed_back_to_selection\", \"Revenir au choix du document\")}\n </Button>\n ) : null\n }\n secondary={\n onContinueWithoutChip ? (\n <Button variant=\"secondary\" onClick={onContinueWithoutChip}>\n {t(\"nfc_scan.failed_continue_without_chip\", \"Poursuivre sans puce\")}\n </Button>\n ) : null\n }\n />\n ) : canGoBack ? (\n <Button variant=\"secondary\" onClick={onBack}>\n {t(\"common.back\", \"Retour\")}\n </Button>\n ) : null;\n\n return (\n <MobilePageLayout footer={footer}>\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 <div className=\"text-center space-y-4 mt-16 md:mt-20\">\n <Title className=\"text-xl md:text-2xl lg:text-3xl\">{title}</Title>\n <Subtitle className=\"text-sm md:text-base text-gray-600 leading-relaxed whitespace-pre-line\">\n {subtitle}\n </Subtitle>\n </div>\n\n <div className=\"flex-1 flex flex-col justify-center items-center gap-4\">\n {status === \"success\" ? (\n <NfcSuccessAnimation />\n ) : status === \"completed\" ? (\n <div className=\"flex flex-col items-center gap-4 p-8\">\n <div className=\"animate-spin w-10 h-10 border-2 border-gray-300 border-t-blue-500 rounded-full\" />\n <p className=\"text-sm text-gray-600\">\n {t(\"nfc_scan.verifying\", \"Vérification en cours…\")}\n </p>\n </div>\n ) : status === \"failed\" ? (\n <div className=\"flex flex-col items-center gap-2 p-8 text-center\">\n <p className=\"text-base font-semibold text-red-600\">\n {t(\"nfc_scan.failed_title\", \"Le scan n'a pas pu être vérifié\")}\n </p>\n <p className=\"text-sm text-gray-600 leading-relaxed\">\n {t(\n \"nfc_scan.failed_description\",\n \"Après plusieurs tentatives, la lecture de votre document n'a pas pu être validée.\"\n )}\n </p>\n {resetError && (\n <p className=\"text-sm text-red-600\" role=\"alert\">\n {resetError}\n </p>\n )}\n </div>\n ) : (\n // Contenu principal QR/mobile — reste monté pour \"pending\" ET \"opened\", pour ne\n // jamais démonter le canvas déjà généré.\n <>\n {qrError ? (\n <div className=\"text-red-500 text-center p-4\">{qrError}</div>\n ) : isMobile ? (\n <div className=\"flex flex-col items-center gap-4 p-8 text-center\">\n <div className=\"animate-spin w-10 h-10 border-2 border-gray-300 border-t-blue-500 rounded-full\" />\n <p className=\"text-sm text-gray-600\">\n {t(\"nfc_scan.opening_app\", \"Ouverture de l'application…\")}\n </p>\n <a\n href={nfcUrl}\n className=\"text-sm font-medium text-blue-600 underline\"\n >\n {t(\"nfc_scan.open_app_link\", \"Ouvrir l'application\")}\n </a>\n </div>\n ) : (\n <div className=\"bg-white p-4 rounded-lg shadow-lg border-2 border-gray-200\">\n <canvas ref={canvasRef} />\n </div>\n )}\n\n {status === \"opened\" && (\n <div className=\"text-center space-y-1\">\n <p\n className=\"text-sm font-semibold\"\n style={{ color: \"var(--uni-secondary-color, #0a9983)\" }}\n >\n {t(\"nfc_scan.qr_opened_title\", \"QR code scanné\")}\n </p>\n <p className=\"text-xs text-gray-500 leading-relaxed\">\n {t(\n \"nfc_scan.qr_opened_description\",\n \"L'expérience NFC est ouverte sur votre smartphone. Continuez le scan sur votre téléphone.\"\n )}\n </p>\n <div className=\"flex items-center justify-center gap-2 text-xs text-gray-500 pt-1\">\n <span\n className=\"h-2 w-2 rounded-full animate-pulse\"\n style={{ backgroundColor: \"var(--uni-primary-color, #11e5c5)\" }}\n />\n {t(\"nfc_scan.waiting_completion\", \"En attente de la lecture NFC\")}\n </div>\n </div>\n )}\n </>\n )}\n </div>\n </div>\n </div>\n </MobilePageLayout>\n );\n};\n\nexport default NfcScanStep;\n"],"names":["useI18n","useRef","useState","useMemo","isMobileDevice","getBaseURL","useNfcSseStatus","useEffect","__awaiter","resetNfcScan","_jsx","PageActions","Button","MobilePageLayout","_jsxs","Title","Subtitle","NfcSuccessAnimation","_Fragment"],"mappings":"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"}
|
|
@@ -75,7 +75,7 @@ var normalizeNfcMode = function (mode) {
|
|
|
75
75
|
*/
|
|
76
76
|
var DocumentCheck = function (_a) {
|
|
77
77
|
var _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
|
|
78
|
-
var stepObject = _a.stepObject, sessionId = _a.sessionId, node = _a.node
|
|
78
|
+
var stepObject = _a.stepObject, sessionId = _a.sessionId, node = _a.node, documentTypeId = _a.documentTypeId, acceptedCountries = _a.acceptedCountries, _p = _a.isMobileCapture, isMobileCapture = _p === void 0 ? false : _p, allowedAddingMethods = _a.allowedAddingMethods, template = _a.template, setUserInput = _a.setUserInput;
|
|
79
79
|
var t = useI18n.useI18n().t;
|
|
80
80
|
var session = SessionContext.getSession().session;
|
|
81
81
|
var _q = React.useState(), documentTemplates = _q[0], setDocumentTemplates = _q[1];
|
|
@@ -142,21 +142,61 @@ var DocumentCheck = function (_a) {
|
|
|
142
142
|
setRetryCount(sessionRetryCount);
|
|
143
143
|
}
|
|
144
144
|
}, [session, node.id]);
|
|
145
|
-
// Restauration après refresh
|
|
146
|
-
//
|
|
147
|
-
//
|
|
145
|
+
// Restauration après refresh / déchargement de l'onglet : `docStep` est en mémoire et serait perdu
|
|
146
|
+
// au rechargement. On s'appuie sur `session.nfcStatus` (persisté et exposé par le backend) pour
|
|
147
|
+
// remettre l'utilisateur au bon endroit du nœud NFC au lieu de repartir au début (gate -1).
|
|
148
|
+
//
|
|
149
|
+
// En parcours full mobile, l'ouverture de l'app de scan peut décharger l'onglet ; au retour, le
|
|
150
|
+
// composant remonte. Sans cette restauration, l'utilisateur retombait sur le choix pays/document.
|
|
148
151
|
var hasRestoredNfcStep = React.useRef(false);
|
|
149
152
|
React.useEffect(function () {
|
|
153
|
+
var _a, _b;
|
|
150
154
|
if (hasRestoredNfcStep.current)
|
|
151
155
|
return;
|
|
152
156
|
if (node.nfcEnabled !== true)
|
|
153
157
|
return;
|
|
154
158
|
var status = session === null || session === void 0 ? void 0 : session.nfcStatus;
|
|
155
|
-
|
|
156
|
-
|
|
159
|
+
// 'pending' (ou absent) → rien à restaurer : le scan n'a pas démarré, l'effet de reset affiche
|
|
160
|
+
// le gate -1 (écran d'entrée normal).
|
|
161
|
+
if (status !== "opened" && status !== "failed" && status !== "completed" && status !== "skipped") {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
// À partir d'ici on prend la décision une seule fois (idempotence, y compris si l'effet se
|
|
165
|
+
// re-déclenche) : on pose le flag AVANT toute navigation.
|
|
166
|
+
hasRestoredNfcStep.current = true;
|
|
167
|
+
if (status === "opened" || status === "failed") {
|
|
168
|
+
// Scan en cours, ou échoué (définitivement, ou en attente d'une nouvelle tentative) :
|
|
169
|
+
// rester sur l'écran de scan / d'échec (docStep 7) avec ses actions.
|
|
157
170
|
setDocStep(7);
|
|
171
|
+
return;
|
|
158
172
|
}
|
|
159
|
-
|
|
173
|
+
if (status === "completed") {
|
|
174
|
+
// Scan réussi avant le déchargement : le nœud NFC est terminé → passer à l'étape suivante
|
|
175
|
+
// (y compris en mode nfcOrPhoto — le NFC réussi suffit à valider le document).
|
|
176
|
+
if (template) {
|
|
177
|
+
stepObject.goToNextStep(node.id, template);
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
stepObject.setStep(stepObject.step + 1);
|
|
181
|
+
}
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
// status === "skipped" : l'utilisateur a poursuivi sans puce avant le déchargement.
|
|
185
|
+
// Reproduit le routage de handleNfcFallback selon le mode :
|
|
186
|
+
// - nfcOrPhoto → reprendre le flux photo/API du même nœud (intro/pays)
|
|
187
|
+
// - nfcOnly → sauter le nœud (étape suivante)
|
|
188
|
+
if (normalizeNfcMode(node.nfcMode) === "nfcOrPhoto") {
|
|
189
|
+
var hasIntroduction = ((_a = node.introductionPage) === null || _a === void 0 ? void 0 : _a.title) !== undefined ||
|
|
190
|
+
((_b = node.introductionPage) === null || _b === void 0 ? void 0 : _b.description) !== undefined;
|
|
191
|
+
setDocStep(hasIntroduction ? 0 : 1);
|
|
192
|
+
}
|
|
193
|
+
else if (template) {
|
|
194
|
+
stepObject.goToNextStep(node.id, template);
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
stepObject.setStep(stepObject.step + 1);
|
|
198
|
+
}
|
|
199
|
+
}, [session, node, template, stepObject]);
|
|
160
200
|
// Check retry limits on mount/reload - if retries exhausted, show error page immediately
|
|
161
201
|
React.useEffect(function () {
|
|
162
202
|
// Special case: if allowResubmission is false and retryCount > 0, show error immediately
|
|
@@ -272,8 +312,18 @@ var DocumentCheck = function (_a) {
|
|
|
272
312
|
if (docStep === 6) {
|
|
273
313
|
return;
|
|
274
314
|
}
|
|
275
|
-
// En NFC, on affiche d'abord l'écran de confirmation de la puce (gate -1)
|
|
315
|
+
// En NFC, on affiche d'abord l'écran de confirmation de la puce (gate -1).
|
|
316
|
+
// MAIS si un scan est déjà engagé/terminé/sauté côté backend (reload / retour après
|
|
317
|
+
// déchargement de l'onglet), on ne force PAS le gate : on laisse l'effet de restauration
|
|
318
|
+
// ci-dessus décider (sinon il l'écraserait et l'utilisateur repartirait au début du nœud).
|
|
276
319
|
if (node.nfcEnabled === true) {
|
|
320
|
+
var nfcStatus = session === null || session === void 0 ? void 0 : session.nfcStatus;
|
|
321
|
+
if (nfcStatus === "opened" ||
|
|
322
|
+
nfcStatus === "failed" ||
|
|
323
|
+
nfcStatus === "completed" ||
|
|
324
|
+
nfcStatus === "skipped") {
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
277
327
|
setDocStep(-1);
|
|
278
328
|
return;
|
|
279
329
|
}
|
|
@@ -284,7 +334,9 @@ var DocumentCheck = function (_a) {
|
|
|
284
334
|
else {
|
|
285
335
|
setDocStep(0);
|
|
286
336
|
}
|
|
287
|
-
|
|
337
|
+
// `session` (nfcStatus) est requis : au chargement asynchrone de la session après un reload,
|
|
338
|
+
// cet effet doit se ré-évaluer pour ne pas forcer le gate -1 alors qu'un scan est déjà engagé.
|
|
339
|
+
}, [documentTypeId, sessionId, isMobileCapture, session, node.nfcEnabled]);
|
|
288
340
|
var handleCountrySelect = function (countryId) {
|
|
289
341
|
console.debug("[DocumentCheck] handleCountrySelect - countryId:", countryId);
|
|
290
342
|
setUserInput(function (prev) { return (tslib_es6.__assign(tslib_es6.__assign({}, prev), { countryCode: countryId.toUpperCase() })); });
|
|
@@ -652,7 +704,7 @@ var DocumentCheck = function (_a) {
|
|
|
652
704
|
? undefined
|
|
653
705
|
: ((_o = node.documentSelection) === null || _o === void 0 ? void 0 : _o.description) ||
|
|
654
706
|
t("jdi.document_selection.description"), nfcEnabled: node.nfcEnabled === true }));
|
|
655
|
-
case 3:
|
|
707
|
+
case 3: {
|
|
656
708
|
if (!selectedDocumentType) {
|
|
657
709
|
console.error("DocumentCheck: selectedDocumentType is null for step 3!");
|
|
658
710
|
return (jsxRuntime.jsxs("div", { className: "flex flex-col items-center justify-center h-full p-4 text-center", children: [jsxRuntime.jsx("div", { className: "text-red-500 text-4xl mb-4", children: "\u26A0\uFE0F" }), jsxRuntime.jsx("h2", { className: "text-xl font-bold text-red-600 mb-2", children: t("document_check.selection_error.title", "Erreur de sélection") }), jsxRuntime.jsx("p", { className: "text-gray-600 mb-4", children: t("document_check.selection_error.description", "Aucun type de document sélectionné. Veuillez revenir à l'étape précédente.") }), jsxRuntime.jsx(ButtonDesktop.default, { className: "px-4 py-2 bg-primary text-white rounded hover:bg-primary-dark transition-colors", type: "back", onClick: function () { return setDocStep(2); }, children: t("document_check.selection_error.back", "Retour à la sélection") })] }));
|
|
@@ -704,6 +756,8 @@ var DocumentCheck = function (_a) {
|
|
|
704
756
|
}
|
|
705
757
|
}, initialFront: errorCode === "2.5" ? fileUploaded === null || fileUploaded === void 0 ? void 0 : fileUploaded.front : null, initialBack: errorCode === "2.5" ? fileUploaded === null || fileUploaded === void 0 ? void 0 : fileUploaded.back : null, sessionId: sessionId }));
|
|
706
758
|
}
|
|
759
|
+
}
|
|
760
|
+
// fall-through volontaire vers case 4 (comportement existant conservé)
|
|
707
761
|
case 4:
|
|
708
762
|
return (jsxRuntime.jsx(JDIProcessing.default, { documentType: (selectedDocumentType === null || selectedDocumentType === void 0 ? void 0 : selectedDocumentType.id) || documentTypeId, onProcessingComplete: handleProcessingComplete, fileUploaded: fileUploaded, documentTypeId: documentTypeId, retryCount: retryCount, documentTemplateId: selectedDocumentType === null || selectedDocumentType === void 0 ? void 0 : selectedDocumentType.documentTemplateId, nodeId: node.id }));
|
|
709
763
|
case 5:
|
|
@@ -716,12 +770,18 @@ var DocumentCheck = function (_a) {
|
|
|
716
770
|
}
|
|
717
771
|
return (jsxRuntime.jsx(NfcScanNode.default, { sessionId: sessionId, nodeId: node.id, stepObject: stepObject, template: template, onBack: function () {
|
|
718
772
|
var _a, _b;
|
|
719
|
-
// Retour vers la sélection du document (corriger un mauvais choix
|
|
720
|
-
// NfcScanStep ferme le SSE via le cleanup du hook (pas d'appel backend
|
|
773
|
+
// Retour vers la sélection du document (corriger un mauvais choix, ou après échec NFC).
|
|
774
|
+
// Le démontage de NfcScanStep ferme le SSE via le cleanup du hook (pas d'appel backend).
|
|
721
775
|
var hasIntroduction = ((_a = node.introductionPage) === null || _a === void 0 ? void 0 : _a.title) !== undefined ||
|
|
722
776
|
((_b = node.introductionPage) === null || _b === void 0 ? void 0 : _b.description) !== undefined;
|
|
723
777
|
hasRestoredNfcStep.current = true; // ne pas re-restaurer l'étape QR après ce retour
|
|
724
778
|
setDocStep(hasIntroduction ? 0 : 1);
|
|
779
|
+
}, onContinueWithoutChip: function () {
|
|
780
|
+
// "Poursuivre sans puce" après échec définitif : ouvre le questionnaire de raison
|
|
781
|
+
// (docStep -2 → NfcFallbackSurvey → handleNfcFallback). handleNfcFallback applique
|
|
782
|
+
// ensuite la reprise selon nfcMode (nfcOrPhoto → photo/API ; nfcOnly → nœud suivant).
|
|
783
|
+
hasRestoredNfcStep.current = true;
|
|
784
|
+
setDocStep(-2);
|
|
725
785
|
} }));
|
|
726
786
|
default:
|
|
727
787
|
console.error("Invalid docStep: ".concat(docStep));
|