@koraidv/react 1.7.11 → 1.8.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.
- package/dist/index.d.mts +55 -6
- package/dist/index.d.ts +55 -6
- package/dist/index.js +825 -379
- package/dist/index.mjs +828 -382
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -47,7 +47,8 @@ function useKoraIDV() {
|
|
|
47
47
|
currentChallenge: null,
|
|
48
48
|
completedChallenges: 0,
|
|
49
49
|
isLoading: false,
|
|
50
|
-
error: null
|
|
50
|
+
error: null,
|
|
51
|
+
lastChallengeError: null
|
|
51
52
|
});
|
|
52
53
|
const [selectedDocumentType, setSelectedDocumentType] = useState(null);
|
|
53
54
|
const [documentFrontCaptured, setDocumentFrontCaptured] = useState(false);
|
|
@@ -246,14 +247,20 @@ function useKoraIDV() {
|
|
|
246
247
|
...prev,
|
|
247
248
|
completedChallenges: nextIndex,
|
|
248
249
|
currentChallenge: nextChallenge,
|
|
249
|
-
isLoading: false
|
|
250
|
+
isLoading: false,
|
|
251
|
+
// Clear any prior retake message — the user just succeeded.
|
|
252
|
+
lastChallengeError: null
|
|
250
253
|
}));
|
|
251
254
|
if (!nextChallenge) {
|
|
252
255
|
setState((prev) => ({ ...prev, step: "processing" }));
|
|
253
256
|
}
|
|
254
257
|
return true;
|
|
255
258
|
}
|
|
256
|
-
setState((prev) => ({
|
|
259
|
+
setState((prev) => ({
|
|
260
|
+
...prev,
|
|
261
|
+
isLoading: false,
|
|
262
|
+
lastChallengeError: retakeMessageForChallenge(currentChallenge.type)
|
|
263
|
+
}));
|
|
257
264
|
return false;
|
|
258
265
|
} catch (error) {
|
|
259
266
|
setState((prev) => ({
|
|
@@ -304,7 +311,8 @@ function useKoraIDV() {
|
|
|
304
311
|
currentChallenge: null,
|
|
305
312
|
completedChallenges: 0,
|
|
306
313
|
isLoading: false,
|
|
307
|
-
error: null
|
|
314
|
+
error: null,
|
|
315
|
+
lastChallengeError: null
|
|
308
316
|
});
|
|
309
317
|
}, [sdk]);
|
|
310
318
|
const retry = useCallback(() => {
|
|
@@ -331,9 +339,27 @@ function useKoraIDV() {
|
|
|
331
339
|
sdk
|
|
332
340
|
};
|
|
333
341
|
}
|
|
342
|
+
function retakeMessageForChallenge(type) {
|
|
343
|
+
switch (type) {
|
|
344
|
+
case "blink":
|
|
345
|
+
return "We didn't catch the blink \u2014 close both eyes briefly and try again.";
|
|
346
|
+
case "smile":
|
|
347
|
+
return "We didn't catch the smile \u2014 show your teeth and try again.";
|
|
348
|
+
case "turn_left":
|
|
349
|
+
return "Turn your head a bit further to the left and try again.";
|
|
350
|
+
case "turn_right":
|
|
351
|
+
return "Turn your head a bit further to the right and try again.";
|
|
352
|
+
case "nod_up":
|
|
353
|
+
return "Tilt your head a bit higher and try again.";
|
|
354
|
+
case "nod_down":
|
|
355
|
+
return "Tilt your head a bit lower and try again.";
|
|
356
|
+
default:
|
|
357
|
+
return "That attempt didn't pass \u2014 follow the prompt and try again.";
|
|
358
|
+
}
|
|
359
|
+
}
|
|
334
360
|
|
|
335
361
|
// src/components/VerificationFlow.tsx
|
|
336
|
-
import { useEffect as
|
|
362
|
+
import { useEffect as useEffect11, useState as useState9 } from "react";
|
|
337
363
|
import { KoraError as KoraError2, KoraErrorCode } from "@koraidv/core";
|
|
338
364
|
|
|
339
365
|
// src/components/styles.ts
|
|
@@ -400,6 +426,36 @@ function injectKeyframes() {
|
|
|
400
426
|
from { opacity: 0; transform: translateY(8px); }
|
|
401
427
|
to { opacity: 1; transform: translateY(0); }
|
|
402
428
|
}
|
|
429
|
+
/* \u2500\u2500\u2500 VisualGuides motion (v1.8.0) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */
|
|
430
|
+
@keyframes kora-head-turn-right {
|
|
431
|
+
0%, 100% { transform: rotate(0deg); }
|
|
432
|
+
50% { transform: rotate(22deg); }
|
|
433
|
+
}
|
|
434
|
+
@keyframes kora-head-turn-left {
|
|
435
|
+
0%, 100% { transform: rotate(0deg); }
|
|
436
|
+
50% { transform: rotate(-22deg); }
|
|
437
|
+
}
|
|
438
|
+
@keyframes kora-head-tilt-up {
|
|
439
|
+
0%, 100% { transform: translateY(0); }
|
|
440
|
+
50% { transform: translateY(-6px) rotate(-6deg); }
|
|
441
|
+
}
|
|
442
|
+
@keyframes kora-head-tilt-down {
|
|
443
|
+
0%, 100% { transform: translateY(0); }
|
|
444
|
+
50% { transform: translateY(6px) rotate(6deg); }
|
|
445
|
+
}
|
|
446
|
+
@keyframes kora-smile {
|
|
447
|
+
0%, 100% { transform: scaleY(0.5); }
|
|
448
|
+
50% { transform: scaleY(1.2); }
|
|
449
|
+
}
|
|
450
|
+
@keyframes kora-blink {
|
|
451
|
+
0%, 80%, 100% { transform: scaleY(1); }
|
|
452
|
+
88% { transform: scaleY(0.05); }
|
|
453
|
+
}
|
|
454
|
+
@keyframes kora-nfc-wave {
|
|
455
|
+
0% { opacity: 0; transform: translateX(-4px); }
|
|
456
|
+
40% { opacity: 1; }
|
|
457
|
+
100% { opacity: 0; transform: translateX(6px); }
|
|
458
|
+
}
|
|
403
459
|
`;
|
|
404
460
|
document.head.appendChild(style);
|
|
405
461
|
}
|
|
@@ -1313,7 +1369,7 @@ var styles = {
|
|
|
1313
1369
|
};
|
|
1314
1370
|
|
|
1315
1371
|
// src/components/DesignSystem.tsx
|
|
1316
|
-
import { useEffect as useEffect2 } from "react";
|
|
1372
|
+
import { useEffect as useEffect2, useState as useState2 } from "react";
|
|
1317
1373
|
import { jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
1318
1374
|
function StepProgressBar({ total, current, isDark = false }) {
|
|
1319
1375
|
return /* @__PURE__ */ jsx2("div", { style: styles.progressBar, children: Array.from({ length: total }).map((_, i) => /* @__PURE__ */ jsx2(
|
|
@@ -1386,10 +1442,26 @@ function ScoreMetricRow({ label, score, icon, status, message }) {
|
|
|
1386
1442
|
}
|
|
1387
1443
|
);
|
|
1388
1444
|
}
|
|
1389
|
-
|
|
1445
|
+
var DEFAULT_AUTO_STEPS = [
|
|
1446
|
+
"Document analyzed",
|
|
1447
|
+
"Checking face match",
|
|
1448
|
+
"Finalizing results"
|
|
1449
|
+
];
|
|
1450
|
+
function ProcessingScreen({ steps, autoAdvance = true }) {
|
|
1390
1451
|
useEffect2(() => {
|
|
1391
1452
|
injectKeyframes();
|
|
1392
1453
|
}, []);
|
|
1454
|
+
const [autoIndex, setAutoIndex] = useState2(0);
|
|
1455
|
+
useEffect2(() => {
|
|
1456
|
+
if (steps || !autoAdvance) return;
|
|
1457
|
+
if (autoIndex >= DEFAULT_AUTO_STEPS.length - 1) return;
|
|
1458
|
+
const t = setTimeout(() => setAutoIndex((i) => i + 1), 1400);
|
|
1459
|
+
return () => clearTimeout(t);
|
|
1460
|
+
}, [autoIndex, steps, autoAdvance]);
|
|
1461
|
+
const renderedSteps = steps ? steps : DEFAULT_AUTO_STEPS.map((label, i) => ({
|
|
1462
|
+
label,
|
|
1463
|
+
status: i < autoIndex ? "done" : i === autoIndex ? "active" : "pending"
|
|
1464
|
+
}));
|
|
1393
1465
|
return /* @__PURE__ */ jsxs("div", { style: styles.processingContainer, children: [
|
|
1394
1466
|
/* @__PURE__ */ jsxs("div", { style: styles.spinnerContainer, children: [
|
|
1395
1467
|
/* @__PURE__ */ jsx2(
|
|
@@ -1440,7 +1512,7 @@ function ProcessingScreen({ steps }) {
|
|
|
1440
1512
|
}
|
|
1441
1513
|
)
|
|
1442
1514
|
] }),
|
|
1443
|
-
/* @__PURE__ */ jsx2("div", { style: styles.processingSteps, children:
|
|
1515
|
+
/* @__PURE__ */ jsx2("div", { style: styles.processingSteps, children: renderedSteps.map((step, i) => /* @__PURE__ */ jsxs("div", { style: styles.processingStep, children: [
|
|
1444
1516
|
/* @__PURE__ */ jsx2(
|
|
1445
1517
|
"div",
|
|
1446
1518
|
{
|
|
@@ -1465,6 +1537,7 @@ function ProcessingScreen({ steps }) {
|
|
|
1465
1537
|
] });
|
|
1466
1538
|
}
|
|
1467
1539
|
function computeScoreBreakdown(verification) {
|
|
1540
|
+
const source = verification.metadata?.source ?? "";
|
|
1468
1541
|
const livenessPercent = Math.round(
|
|
1469
1542
|
verification.scores?.liveness ?? verification.livenessVerification?.livenessScore ?? 0
|
|
1470
1543
|
);
|
|
@@ -1477,9 +1550,12 @@ function computeScoreBreakdown(verification) {
|
|
|
1477
1550
|
const selfiePercent = Math.round(
|
|
1478
1551
|
verification.scores?.faceMatch ?? verification.faceVerification?.matchScore ?? 0
|
|
1479
1552
|
);
|
|
1553
|
+
const isWeb = source === "web";
|
|
1554
|
+
const passFloor = isWeb ? 65 : 75;
|
|
1555
|
+
const borderlineFloor = isWeb ? 40 : 50;
|
|
1480
1556
|
function getStatus(score) {
|
|
1481
|
-
if (score >=
|
|
1482
|
-
if (score >=
|
|
1557
|
+
if (score >= passFloor) return "pass";
|
|
1558
|
+
if (score >= borderlineFloor) return "borderline";
|
|
1483
1559
|
return "fail";
|
|
1484
1560
|
}
|
|
1485
1561
|
function getMessage(status) {
|
|
@@ -1608,11 +1684,11 @@ function ConsentItem({
|
|
|
1608
1684
|
}
|
|
1609
1685
|
|
|
1610
1686
|
// src/components/CountrySelectionScreen.tsx
|
|
1611
|
-
import { useState as
|
|
1687
|
+
import { useState as useState3, useMemo as useMemo2 } from "react";
|
|
1612
1688
|
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1613
1689
|
function CountrySelectionScreen({ countries, onSelect, onCancel }) {
|
|
1614
|
-
const [selected, setSelected] =
|
|
1615
|
-
const [searchQuery, setSearchQuery] =
|
|
1690
|
+
const [selected, setSelected] = useState3(null);
|
|
1691
|
+
const [searchQuery, setSearchQuery] = useState3("");
|
|
1616
1692
|
const filteredCountries = useMemo2(() => {
|
|
1617
1693
|
const countryList = countries || [];
|
|
1618
1694
|
if (!searchQuery.trim()) return countryList;
|
|
@@ -1762,8 +1838,243 @@ function getIcon(type) {
|
|
|
1762
1838
|
}
|
|
1763
1839
|
|
|
1764
1840
|
// src/components/DocumentCaptureScreen.tsx
|
|
1765
|
-
import { useRef as
|
|
1766
|
-
|
|
1841
|
+
import { useRef as useRef3, useEffect as useEffect5, useState as useState5, useCallback as useCallback2 } from "react";
|
|
1842
|
+
|
|
1843
|
+
// src/components/VisualGuides.tsx
|
|
1844
|
+
import { useEffect as useEffect3 } from "react";
|
|
1845
|
+
import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1846
|
+
function visualGuideForChallenge(challengeType) {
|
|
1847
|
+
switch (challengeType) {
|
|
1848
|
+
case "turn_left":
|
|
1849
|
+
return "livenessTurnLeft";
|
|
1850
|
+
case "turn_right":
|
|
1851
|
+
return "livenessTurnRight";
|
|
1852
|
+
case "nod_up":
|
|
1853
|
+
return "livenessLookUp";
|
|
1854
|
+
case "nod_down":
|
|
1855
|
+
return "livenessLookDown";
|
|
1856
|
+
case "smile":
|
|
1857
|
+
return "livenessSmile";
|
|
1858
|
+
case "blink":
|
|
1859
|
+
return "livenessBlink";
|
|
1860
|
+
default:
|
|
1861
|
+
return null;
|
|
1862
|
+
}
|
|
1863
|
+
}
|
|
1864
|
+
function VisualGuide({ kind, size = 96 }) {
|
|
1865
|
+
useEffect3(() => {
|
|
1866
|
+
injectKeyframes();
|
|
1867
|
+
}, []);
|
|
1868
|
+
const common = { width: size, height: size, viewBox: "0 0 100 100" };
|
|
1869
|
+
const fg = colors.teal;
|
|
1870
|
+
const dim = "rgba(255,255,255,0.3)";
|
|
1871
|
+
switch (kind) {
|
|
1872
|
+
case "docFront":
|
|
1873
|
+
return /* @__PURE__ */ jsx6(DocFront, { ...common, fg, dim });
|
|
1874
|
+
case "docBack":
|
|
1875
|
+
return /* @__PURE__ */ jsx6(DocBack, { ...common, fg, dim });
|
|
1876
|
+
case "selfie":
|
|
1877
|
+
return /* @__PURE__ */ jsx6(Selfie, { ...common, fg, dim });
|
|
1878
|
+
case "nfcScan":
|
|
1879
|
+
return /* @__PURE__ */ jsx6(NfcScan, { ...common, fg, dim });
|
|
1880
|
+
case "livenessTurnLeft":
|
|
1881
|
+
return /* @__PURE__ */ jsx6(HeadTurn, { ...common, fg, dim, right: false });
|
|
1882
|
+
case "livenessTurnRight":
|
|
1883
|
+
return /* @__PURE__ */ jsx6(HeadTurn, { ...common, fg, dim, right: true });
|
|
1884
|
+
case "livenessLookUp":
|
|
1885
|
+
return /* @__PURE__ */ jsx6(HeadTilt, { ...common, fg, dim, up: true });
|
|
1886
|
+
case "livenessLookDown":
|
|
1887
|
+
return /* @__PURE__ */ jsx6(HeadTilt, { ...common, fg, dim, up: false });
|
|
1888
|
+
case "livenessSmile":
|
|
1889
|
+
return /* @__PURE__ */ jsx6(Smile, { ...common, fg, dim });
|
|
1890
|
+
case "livenessBlink":
|
|
1891
|
+
return /* @__PURE__ */ jsx6(Blink, { ...common, fg, dim });
|
|
1892
|
+
}
|
|
1893
|
+
}
|
|
1894
|
+
function DocFront({ width, height, viewBox, fg, dim }) {
|
|
1895
|
+
return /* @__PURE__ */ jsxs5("svg", { width, height, viewBox, fill: "none", children: [
|
|
1896
|
+
/* @__PURE__ */ jsx6("rect", { x: "8", y: "26", width: "84", height: "48", rx: "5", stroke: fg, strokeWidth: "2.5" }),
|
|
1897
|
+
/* @__PURE__ */ jsx6("rect", { x: "14", y: "34", width: "22", height: "28", rx: "2", fill: dim }),
|
|
1898
|
+
/* @__PURE__ */ jsx6("rect", { x: "42", y: "36", width: "44", height: "3", rx: "1.5", fill: fg }),
|
|
1899
|
+
/* @__PURE__ */ jsx6("rect", { x: "42", y: "44", width: "36", height: "2.5", rx: "1.25", fill: dim }),
|
|
1900
|
+
/* @__PURE__ */ jsx6("rect", { x: "42", y: "50", width: "40", height: "2.5", rx: "1.25", fill: dim }),
|
|
1901
|
+
/* @__PURE__ */ jsx6("rect", { x: "42", y: "56", width: "30", height: "2.5", rx: "1.25", fill: dim })
|
|
1902
|
+
] });
|
|
1903
|
+
}
|
|
1904
|
+
function DocBack({ width, height, viewBox, fg, dim }) {
|
|
1905
|
+
return /* @__PURE__ */ jsxs5("svg", { width, height, viewBox, fill: "none", children: [
|
|
1906
|
+
/* @__PURE__ */ jsx6("rect", { x: "8", y: "26", width: "84", height: "48", rx: "5", stroke: fg, strokeWidth: "2.5" }),
|
|
1907
|
+
[16, 19, 22, 26, 28, 32, 35, 39, 42, 46, 49, 53, 56, 60].map((x, i) => /* @__PURE__ */ jsx6(
|
|
1908
|
+
"rect",
|
|
1909
|
+
{
|
|
1910
|
+
x,
|
|
1911
|
+
y: "34",
|
|
1912
|
+
width: i % 3 === 0 ? 2 : 1.2,
|
|
1913
|
+
height: "20",
|
|
1914
|
+
fill: fg
|
|
1915
|
+
},
|
|
1916
|
+
i
|
|
1917
|
+
)),
|
|
1918
|
+
/* @__PURE__ */ jsx6("line", { x1: "14", y1: "64", x2: "58", y2: "64", stroke: dim, strokeWidth: "1.5" }),
|
|
1919
|
+
/* @__PURE__ */ jsx6("rect", { x: "66", y: "34", width: "20", height: "20", rx: "1", fill: dim })
|
|
1920
|
+
] });
|
|
1921
|
+
}
|
|
1922
|
+
function Selfie({ width, height, viewBox, fg, dim }) {
|
|
1923
|
+
return /* @__PURE__ */ jsxs5("svg", { width, height, viewBox, fill: "none", children: [
|
|
1924
|
+
/* @__PURE__ */ jsx6("ellipse", { cx: "50", cy: "42", rx: "22", ry: "28", stroke: fg, strokeWidth: "2.5" }),
|
|
1925
|
+
/* @__PURE__ */ jsx6("circle", { cx: "42", cy: "38", r: "2.5", fill: fg }),
|
|
1926
|
+
/* @__PURE__ */ jsx6("circle", { cx: "58", cy: "38", r: "2.5", fill: fg }),
|
|
1927
|
+
/* @__PURE__ */ jsx6("path", { d: "M 40 50 Q 50 56 60 50", stroke: fg, strokeWidth: "2", strokeLinecap: "round", fill: "none" }),
|
|
1928
|
+
/* @__PURE__ */ jsx6("path", { d: "M 18 92 Q 18 76 36 70 L 64 70 Q 82 76 82 92", stroke: dim, strokeWidth: "2", fill: "none" })
|
|
1929
|
+
] });
|
|
1930
|
+
}
|
|
1931
|
+
function NfcScan({ width, height, viewBox, fg, dim }) {
|
|
1932
|
+
return /* @__PURE__ */ jsxs5("svg", { width, height, viewBox, fill: "none", children: [
|
|
1933
|
+
/* @__PURE__ */ jsx6("rect", { x: "14", y: "40", width: "40", height: "50", rx: "3", stroke: dim, strokeWidth: "2" }),
|
|
1934
|
+
/* @__PURE__ */ jsx6("circle", { cx: "34", cy: "60", r: "6", stroke: dim, strokeWidth: "1.5" }),
|
|
1935
|
+
/* @__PURE__ */ jsx6("rect", { x: "62", y: "22", width: "26", height: "52", rx: "4", stroke: fg, strokeWidth: "2.5" }),
|
|
1936
|
+
/* @__PURE__ */ jsx6("rect", { x: "66", y: "26", width: "18", height: "38", rx: "1.5", fill: dim, opacity: "0.5" }),
|
|
1937
|
+
/* @__PURE__ */ jsx6("path", { d: "M 58 48 Q 52 48 50 56", stroke: fg, strokeWidth: "2", fill: "none", style: { animation: "kora-nfc-wave 1.6s ease-out infinite" } }),
|
|
1938
|
+
/* @__PURE__ */ jsx6("path", { d: "M 58 44 Q 50 44 46 56", stroke: fg, strokeWidth: "2", fill: "none", opacity: "0.7", style: { animation: "kora-nfc-wave 1.6s ease-out infinite 0.3s" } })
|
|
1939
|
+
] });
|
|
1940
|
+
}
|
|
1941
|
+
function HeadTurn({ width, height, viewBox, fg, dim, right }) {
|
|
1942
|
+
const arrowPath = right ? "M 30 12 L 70 12 L 64 6 M 70 12 L 64 18" : "M 70 12 L 30 12 L 36 6 M 30 12 L 36 18";
|
|
1943
|
+
return /* @__PURE__ */ jsxs5("svg", { width, height, viewBox, fill: "none", children: [
|
|
1944
|
+
/* @__PURE__ */ jsx6("path", { d: arrowPath, stroke: fg, strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", fill: "none" }),
|
|
1945
|
+
/* @__PURE__ */ jsxs5(
|
|
1946
|
+
"g",
|
|
1947
|
+
{
|
|
1948
|
+
style: {
|
|
1949
|
+
transformOrigin: "50px 56px",
|
|
1950
|
+
animation: right ? "kora-head-turn-right 2s ease-in-out infinite" : "kora-head-turn-left 2s ease-in-out infinite"
|
|
1951
|
+
},
|
|
1952
|
+
children: [
|
|
1953
|
+
/* @__PURE__ */ jsx6("ellipse", { cx: "50", cy: "55", rx: "20", ry: "26", stroke: fg, strokeWidth: "2.5" }),
|
|
1954
|
+
/* @__PURE__ */ jsx6("circle", { cx: "42", cy: "50", r: "2", fill: fg }),
|
|
1955
|
+
/* @__PURE__ */ jsx6("circle", { cx: "58", cy: "50", r: "2", fill: fg }),
|
|
1956
|
+
/* @__PURE__ */ jsx6("path", { d: "M 50 54 L 50 62", stroke: dim, strokeWidth: "1.5", strokeLinecap: "round" })
|
|
1957
|
+
]
|
|
1958
|
+
}
|
|
1959
|
+
)
|
|
1960
|
+
] });
|
|
1961
|
+
}
|
|
1962
|
+
function HeadTilt({ width, height, viewBox, fg, dim, up }) {
|
|
1963
|
+
const arrowPath = up ? "M 50 92 L 50 14 M 44 22 L 50 14 L 56 22" : "M 50 14 L 50 92 M 44 84 L 50 92 L 56 84";
|
|
1964
|
+
return /* @__PURE__ */ jsxs5("svg", { width, height, viewBox, fill: "none", children: [
|
|
1965
|
+
/* @__PURE__ */ jsx6("path", { d: arrowPath, stroke: fg, strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", opacity: "0.4", fill: "none" }),
|
|
1966
|
+
/* @__PURE__ */ jsxs5(
|
|
1967
|
+
"g",
|
|
1968
|
+
{
|
|
1969
|
+
style: {
|
|
1970
|
+
transformOrigin: "50px 56px",
|
|
1971
|
+
animation: up ? "kora-head-tilt-up 2s ease-in-out infinite" : "kora-head-tilt-down 2s ease-in-out infinite"
|
|
1972
|
+
},
|
|
1973
|
+
children: [
|
|
1974
|
+
/* @__PURE__ */ jsx6("ellipse", { cx: "50", cy: "55", rx: "20", ry: "26", stroke: fg, strokeWidth: "2.5" }),
|
|
1975
|
+
/* @__PURE__ */ jsx6("circle", { cx: "42", cy: "50", r: "2", fill: fg }),
|
|
1976
|
+
/* @__PURE__ */ jsx6("circle", { cx: "58", cy: "50", r: "2", fill: fg }),
|
|
1977
|
+
/* @__PURE__ */ jsx6("path", { d: "M 50 54 L 50 62", stroke: dim, strokeWidth: "1.5", strokeLinecap: "round" })
|
|
1978
|
+
]
|
|
1979
|
+
}
|
|
1980
|
+
)
|
|
1981
|
+
] });
|
|
1982
|
+
}
|
|
1983
|
+
function Smile({ width, height, viewBox, fg, dim }) {
|
|
1984
|
+
return /* @__PURE__ */ jsxs5("svg", { width, height, viewBox, fill: "none", children: [
|
|
1985
|
+
/* @__PURE__ */ jsx6("ellipse", { cx: "50", cy: "50", rx: "24", ry: "30", stroke: fg, strokeWidth: "2.5" }),
|
|
1986
|
+
/* @__PURE__ */ jsx6("circle", { cx: "40", cy: "44", r: "2.5", fill: fg }),
|
|
1987
|
+
/* @__PURE__ */ jsx6("circle", { cx: "60", cy: "44", r: "2.5", fill: fg }),
|
|
1988
|
+
/* @__PURE__ */ jsx6(
|
|
1989
|
+
"path",
|
|
1990
|
+
{
|
|
1991
|
+
d: "M 38 60 Q 50 68 62 60",
|
|
1992
|
+
stroke: fg,
|
|
1993
|
+
strokeWidth: "2.5",
|
|
1994
|
+
strokeLinecap: "round",
|
|
1995
|
+
fill: "none",
|
|
1996
|
+
style: { animation: "kora-smile 2s ease-in-out infinite", transformOrigin: "50px 60px" }
|
|
1997
|
+
}
|
|
1998
|
+
),
|
|
1999
|
+
/* @__PURE__ */ jsx6("line", { x1: "40", y1: "60", x2: "60", y2: "60", stroke: dim, strokeWidth: "1.5", opacity: "0.3" })
|
|
2000
|
+
] });
|
|
2001
|
+
}
|
|
2002
|
+
function Blink({ width, height, viewBox, fg, dim }) {
|
|
2003
|
+
return /* @__PURE__ */ jsxs5("svg", { width, height, viewBox, fill: "none", children: [
|
|
2004
|
+
/* @__PURE__ */ jsx6("ellipse", { cx: "50", cy: "50", rx: "24", ry: "30", stroke: fg, strokeWidth: "2.5" }),
|
|
2005
|
+
/* @__PURE__ */ jsxs5("g", { style: { animation: "kora-blink 1.6s ease-in-out infinite" }, children: [
|
|
2006
|
+
/* @__PURE__ */ jsx6("circle", { cx: "40", cy: "44", r: "3", fill: fg }),
|
|
2007
|
+
/* @__PURE__ */ jsx6("circle", { cx: "60", cy: "44", r: "3", fill: fg })
|
|
2008
|
+
] }),
|
|
2009
|
+
/* @__PURE__ */ jsx6("path", { d: "M 42 60 Q 50 64 58 60", stroke: dim, strokeWidth: "2", strokeLinecap: "round", fill: "none" })
|
|
2010
|
+
] });
|
|
2011
|
+
}
|
|
2012
|
+
|
|
2013
|
+
// src/hooks/useDocumentDetection.ts
|
|
2014
|
+
import { useEffect as useEffect4, useRef as useRef2, useState as useState4 } from "react";
|
|
2015
|
+
var FRAME_INTERVAL_MS = 300;
|
|
2016
|
+
function useDocumentDetection(videoRef, side) {
|
|
2017
|
+
const [signals, setSignals] = useState4({
|
|
2018
|
+
documentDetected: false,
|
|
2019
|
+
detectorActive: false
|
|
2020
|
+
});
|
|
2021
|
+
const detectorRef = useRef2(null);
|
|
2022
|
+
const intervalRef = useRef2(null);
|
|
2023
|
+
useEffect4(() => {
|
|
2024
|
+
let cancelled = false;
|
|
2025
|
+
async function setup() {
|
|
2026
|
+
const win = typeof window !== "undefined" ? window : null;
|
|
2027
|
+
if (!win) return;
|
|
2028
|
+
let DetectorCtor = null;
|
|
2029
|
+
if (side === "front" && "FaceDetector" in win) {
|
|
2030
|
+
DetectorCtor = win["FaceDetector"];
|
|
2031
|
+
} else if (side === "back" && "BarcodeDetector" in win) {
|
|
2032
|
+
DetectorCtor = win["BarcodeDetector"];
|
|
2033
|
+
}
|
|
2034
|
+
if (!DetectorCtor) {
|
|
2035
|
+
return;
|
|
2036
|
+
}
|
|
2037
|
+
try {
|
|
2038
|
+
const detector = new DetectorCtor(
|
|
2039
|
+
side === "front" ? { fastMode: true, maxDetectedFaces: 1 } : { formats: ["pdf417", "qr_code", "data_matrix", "code_128"] }
|
|
2040
|
+
);
|
|
2041
|
+
if (cancelled) return;
|
|
2042
|
+
detectorRef.current = detector;
|
|
2043
|
+
setSignals((prev) => ({ ...prev, detectorActive: true }));
|
|
2044
|
+
} catch {
|
|
2045
|
+
return;
|
|
2046
|
+
}
|
|
2047
|
+
intervalRef.current = setInterval(async () => {
|
|
2048
|
+
const detector = detectorRef.current;
|
|
2049
|
+
const video = videoRef.current;
|
|
2050
|
+
if (!detector || !video || video.readyState < 2) return;
|
|
2051
|
+
try {
|
|
2052
|
+
const results = await detector.detect(video);
|
|
2053
|
+
if (cancelled) return;
|
|
2054
|
+
setSignals((prev) => {
|
|
2055
|
+
const next = results.length > 0;
|
|
2056
|
+
if (prev.documentDetected === next) return prev;
|
|
2057
|
+
return { ...prev, documentDetected: next };
|
|
2058
|
+
});
|
|
2059
|
+
} catch {
|
|
2060
|
+
}
|
|
2061
|
+
}, FRAME_INTERVAL_MS);
|
|
2062
|
+
}
|
|
2063
|
+
setup();
|
|
2064
|
+
return () => {
|
|
2065
|
+
cancelled = true;
|
|
2066
|
+
if (intervalRef.current) {
|
|
2067
|
+
clearInterval(intervalRef.current);
|
|
2068
|
+
intervalRef.current = null;
|
|
2069
|
+
}
|
|
2070
|
+
detectorRef.current = null;
|
|
2071
|
+
};
|
|
2072
|
+
}, [videoRef, side]);
|
|
2073
|
+
return signals;
|
|
2074
|
+
}
|
|
2075
|
+
|
|
2076
|
+
// src/components/DocumentCaptureScreen.tsx
|
|
2077
|
+
import { Fragment, jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1767
2078
|
var qualityIssueMessages = {
|
|
1768
2079
|
face_blurred: "Photo on document is blurry. Retake in better lighting.",
|
|
1769
2080
|
low_resolution: "Image quality too low. Move closer to document.",
|
|
@@ -1780,23 +2091,25 @@ function DocumentCaptureScreen({
|
|
|
1780
2091
|
requiresBack = true,
|
|
1781
2092
|
onQualityCheck,
|
|
1782
2093
|
onCapture,
|
|
1783
|
-
onCancel
|
|
2094
|
+
onCancel,
|
|
2095
|
+
showVisualGuides = true
|
|
1784
2096
|
}) {
|
|
1785
|
-
const videoRef =
|
|
1786
|
-
const canvasRef =
|
|
1787
|
-
const guideRef =
|
|
1788
|
-
const [stream, setStream] =
|
|
1789
|
-
const
|
|
1790
|
-
const [
|
|
1791
|
-
const [
|
|
1792
|
-
const [
|
|
1793
|
-
const [
|
|
1794
|
-
const [
|
|
1795
|
-
const [
|
|
1796
|
-
|
|
2097
|
+
const videoRef = useRef3(null);
|
|
2098
|
+
const canvasRef = useRef3(null);
|
|
2099
|
+
const guideRef = useRef3(null);
|
|
2100
|
+
const [stream, setStream] = useState5(null);
|
|
2101
|
+
const documentSignals = useDocumentDetection(videoRef, side);
|
|
2102
|
+
const [isCapturing, setIsCapturing] = useState5(false);
|
|
2103
|
+
const [error, setError] = useState5(null);
|
|
2104
|
+
const [capturedImage, setCapturedImage] = useState5(null);
|
|
2105
|
+
const [capturedBlob, setCapturedBlob] = useState5(null);
|
|
2106
|
+
const [qualityResult, setQualityResult] = useState5(null);
|
|
2107
|
+
const [isCheckingQuality, setIsCheckingQuality] = useState5(false);
|
|
2108
|
+
const [retakeCount, setRetakeCount] = useState5(0);
|
|
2109
|
+
useEffect5(() => {
|
|
1797
2110
|
injectKeyframes();
|
|
1798
2111
|
}, []);
|
|
1799
|
-
|
|
2112
|
+
useEffect5(() => {
|
|
1800
2113
|
let mounted = true;
|
|
1801
2114
|
async function startCamera() {
|
|
1802
2115
|
try {
|
|
@@ -1818,7 +2131,7 @@ function DocumentCaptureScreen({
|
|
|
1818
2131
|
mounted = false;
|
|
1819
2132
|
};
|
|
1820
2133
|
}, [capturedImage]);
|
|
1821
|
-
|
|
2134
|
+
useEffect5(() => {
|
|
1822
2135
|
return () => {
|
|
1823
2136
|
stream?.getTracks().forEach((t) => t.stop());
|
|
1824
2137
|
};
|
|
@@ -1911,24 +2224,24 @@ function DocumentCaptureScreen({
|
|
|
1911
2224
|
}
|
|
1912
2225
|
};
|
|
1913
2226
|
if (error) {
|
|
1914
|
-
return /* @__PURE__ */
|
|
1915
|
-
/* @__PURE__ */
|
|
1916
|
-
/* @__PURE__ */
|
|
2227
|
+
return /* @__PURE__ */ jsx7("div", { style: styles.container, children: /* @__PURE__ */ jsxs6("div", { style: styles.errorContainer, children: [
|
|
2228
|
+
/* @__PURE__ */ jsx7("p", { style: styles.errorText, children: error }),
|
|
2229
|
+
/* @__PURE__ */ jsx7("button", { style: styles.primaryButton, onClick: onCancel, children: "Go Back" })
|
|
1917
2230
|
] }) });
|
|
1918
2231
|
}
|
|
1919
2232
|
if (capturedImage) {
|
|
1920
2233
|
const qualityPassed = qualityResult && qualityResult.qualityScore >= 60;
|
|
1921
2234
|
const qualityFailed = qualityResult && qualityResult.qualityScore < 60;
|
|
1922
2235
|
const canContinueAnyway = qualityFailed && retakeCount >= 2;
|
|
1923
|
-
return /* @__PURE__ */
|
|
1924
|
-
/* @__PURE__ */
|
|
1925
|
-
/* @__PURE__ */
|
|
1926
|
-
/* @__PURE__ */
|
|
1927
|
-
/* @__PURE__ */
|
|
1928
|
-
/* @__PURE__ */
|
|
2236
|
+
return /* @__PURE__ */ jsxs6("div", { style: styles.darkContainer, children: [
|
|
2237
|
+
/* @__PURE__ */ jsx7(StepProgressBar, { total: 5, current: 3, isDark: true }),
|
|
2238
|
+
/* @__PURE__ */ jsxs6("div", { style: styles.darkScreenHeader, children: [
|
|
2239
|
+
/* @__PURE__ */ jsx7("div", { style: { width: 40 } }),
|
|
2240
|
+
/* @__PURE__ */ jsx7("h1", { style: styles.darkScreenTitle, children: "Review your photo" }),
|
|
2241
|
+
/* @__PURE__ */ jsx7("button", { style: styles.glassCloseButton, onClick: onCancel, children: "\u2715" })
|
|
1929
2242
|
] }),
|
|
1930
|
-
/* @__PURE__ */
|
|
1931
|
-
/* @__PURE__ */
|
|
2243
|
+
/* @__PURE__ */ jsx7("div", { style: { flex: 1, display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", padding: "24px" }, children: /* @__PURE__ */ jsxs6("div", { style: styles.reviewCard, children: [
|
|
2244
|
+
/* @__PURE__ */ jsx7(
|
|
1932
2245
|
"img",
|
|
1933
2246
|
{
|
|
1934
2247
|
src: capturedImage,
|
|
@@ -1936,42 +2249,42 @@ function DocumentCaptureScreen({
|
|
|
1936
2249
|
style: { width: "100%", maxWidth: "300px", borderRadius: "16px", display: "block", margin: "0 auto" }
|
|
1937
2250
|
}
|
|
1938
2251
|
),
|
|
1939
|
-
isCheckingQuality && /* @__PURE__ */
|
|
1940
|
-
qualityPassed && /* @__PURE__ */
|
|
1941
|
-
/* @__PURE__ */
|
|
2252
|
+
isCheckingQuality && /* @__PURE__ */ jsx7("div", { style: { textAlign: "center", marginTop: "16px" }, children: /* @__PURE__ */ jsx7("span", { style: { ...styles.reviewBadge, backgroundColor: "rgba(255,255,255,0.1)" }, children: "Checking quality..." }) }),
|
|
2253
|
+
qualityPassed && /* @__PURE__ */ jsxs6(Fragment, { children: [
|
|
2254
|
+
/* @__PURE__ */ jsx7("div", { style: { textAlign: "center", marginTop: "16px" }, children: /* @__PURE__ */ jsxs6("span", { style: styles.reviewBadge, children: [
|
|
1942
2255
|
"\u2713 Quality score: ",
|
|
1943
2256
|
Math.round(qualityResult.qualityScore),
|
|
1944
2257
|
"%"
|
|
1945
2258
|
] }) }),
|
|
1946
|
-
/* @__PURE__ */
|
|
1947
|
-
/* @__PURE__ */
|
|
1948
|
-
/* @__PURE__ */
|
|
1949
|
-
/* @__PURE__ */
|
|
2259
|
+
/* @__PURE__ */ jsxs6("div", { style: styles.qualityChecks, children: [
|
|
2260
|
+
/* @__PURE__ */ jsx7(QualityCheck, { label: "Sharp" }),
|
|
2261
|
+
/* @__PURE__ */ jsx7(QualityCheck, { label: "Well-lit" }),
|
|
2262
|
+
/* @__PURE__ */ jsx7(QualityCheck, { label: "Readable" })
|
|
1950
2263
|
] })
|
|
1951
2264
|
] }),
|
|
1952
|
-
qualityFailed && /* @__PURE__ */
|
|
1953
|
-
/* @__PURE__ */
|
|
2265
|
+
qualityFailed && /* @__PURE__ */ jsxs6(Fragment, { children: [
|
|
2266
|
+
/* @__PURE__ */ jsx7("div", { style: { textAlign: "center", marginTop: "16px" }, children: /* @__PURE__ */ jsxs6("span", { style: { ...styles.reviewBadge, backgroundColor: "rgba(239,68,68,0.15)", color: "#ef4444" }, children: [
|
|
1954
2267
|
"\u26A0 Quality score: ",
|
|
1955
2268
|
Math.round(qualityResult.qualityScore),
|
|
1956
2269
|
"%"
|
|
1957
2270
|
] }) }),
|
|
1958
|
-
/* @__PURE__ */
|
|
2271
|
+
/* @__PURE__ */ jsx7("div", { style: { padding: "12px 0" }, children: qualityResult.qualityIssues.map((issue, i) => /* @__PURE__ */ jsx7("p", { style: { color: "rgba(255,255,255,0.7)", fontSize: "13px", margin: "4px 0", textAlign: "center" }, children: qualityIssueMessages[issue] || issue }, i)) })
|
|
1959
2272
|
] }),
|
|
1960
|
-
!qualityResult && !isCheckingQuality && /* @__PURE__ */
|
|
1961
|
-
/* @__PURE__ */
|
|
1962
|
-
/* @__PURE__ */
|
|
1963
|
-
/* @__PURE__ */
|
|
1964
|
-
/* @__PURE__ */
|
|
1965
|
-
/* @__PURE__ */
|
|
2273
|
+
!qualityResult && !isCheckingQuality && /* @__PURE__ */ jsxs6(Fragment, { children: [
|
|
2274
|
+
/* @__PURE__ */ jsx7("div", { style: { textAlign: "center", marginTop: "16px" }, children: /* @__PURE__ */ jsx7("span", { style: styles.reviewBadge, children: "\u2713 Good quality" }) }),
|
|
2275
|
+
/* @__PURE__ */ jsxs6("div", { style: styles.qualityChecks, children: [
|
|
2276
|
+
/* @__PURE__ */ jsx7(QualityCheck, { label: "Sharp" }),
|
|
2277
|
+
/* @__PURE__ */ jsx7(QualityCheck, { label: "Well-lit" }),
|
|
2278
|
+
/* @__PURE__ */ jsx7(QualityCheck, { label: "No glare" })
|
|
1966
2279
|
] })
|
|
1967
2280
|
] })
|
|
1968
2281
|
] }) }),
|
|
1969
|
-
/* @__PURE__ */
|
|
1970
|
-
/* @__PURE__ */
|
|
1971
|
-
canContinueAnyway && /* @__PURE__ */
|
|
1972
|
-
] }) : /* @__PURE__ */
|
|
1973
|
-
/* @__PURE__ */
|
|
1974
|
-
/* @__PURE__ */
|
|
2282
|
+
/* @__PURE__ */ jsx7("div", { style: styles.reviewButtonsRow, children: qualityFailed ? /* @__PURE__ */ jsxs6(Fragment, { children: [
|
|
2283
|
+
/* @__PURE__ */ jsx7("button", { style: { ...styles.darkOutlineButton, flex: 1 }, onClick: handleRetake, children: "Retake" }),
|
|
2284
|
+
canContinueAnyway && /* @__PURE__ */ jsx7("button", { style: { ...styles.primaryButton, flex: 1 }, onClick: handleContinueAnyway, children: "Continue anyway" })
|
|
2285
|
+
] }) : /* @__PURE__ */ jsxs6(Fragment, { children: [
|
|
2286
|
+
/* @__PURE__ */ jsx7("button", { style: { ...styles.darkOutlineButton, flex: 1 }, onClick: handleRetake, children: "Retake" }),
|
|
2287
|
+
/* @__PURE__ */ jsx7(
|
|
1975
2288
|
"button",
|
|
1976
2289
|
{
|
|
1977
2290
|
style: { ...styles.primaryButton, flex: 1, opacity: isCheckingQuality ? 0.5 : 1 },
|
|
@@ -1983,29 +2296,54 @@ function DocumentCaptureScreen({
|
|
|
1983
2296
|
] }) })
|
|
1984
2297
|
] });
|
|
1985
2298
|
}
|
|
1986
|
-
return /* @__PURE__ */
|
|
1987
|
-
/* @__PURE__ */
|
|
1988
|
-
/* @__PURE__ */
|
|
1989
|
-
/* @__PURE__ */
|
|
1990
|
-
/* @__PURE__ */
|
|
1991
|
-
/* @__PURE__ */
|
|
1992
|
-
documentType && /* @__PURE__ */
|
|
2299
|
+
return /* @__PURE__ */ jsxs6("div", { style: styles.captureContainer, children: [
|
|
2300
|
+
/* @__PURE__ */ jsx7(StepProgressBar, { total: 5, current: 3, isDark: true }),
|
|
2301
|
+
/* @__PURE__ */ jsxs6("div", { style: styles.darkScreenHeader, children: [
|
|
2302
|
+
/* @__PURE__ */ jsx7("div", { style: { width: 40 } }),
|
|
2303
|
+
/* @__PURE__ */ jsxs6("div", { style: { flex: 1, textAlign: "center" }, children: [
|
|
2304
|
+
/* @__PURE__ */ jsx7("h1", { style: { ...styles.darkScreenTitle, margin: 0 }, children: side === "front" ? "Front of ID" : "Back of ID" }),
|
|
2305
|
+
documentType && /* @__PURE__ */ jsx7("p", { style: styles.darkScreenSubtitle, children: documentType })
|
|
1993
2306
|
] }),
|
|
1994
|
-
/* @__PURE__ */
|
|
2307
|
+
/* @__PURE__ */ jsx7("button", { style: styles.glassCloseButton, onClick: onCancel, children: "\u2715" })
|
|
1995
2308
|
] }),
|
|
1996
|
-
/* @__PURE__ */
|
|
1997
|
-
|
|
1998
|
-
/* @__PURE__ */
|
|
1999
|
-
|
|
2000
|
-
/* @__PURE__ */
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2309
|
+
showVisualGuides && /* @__PURE__ */ jsx7("div", { style: { display: "flex", justifyContent: "center", padding: "6px 0 0" }, children: /* @__PURE__ */ jsx7(VisualGuide, { kind: side === "front" ? "docFront" : "docBack", size: 56 }) }),
|
|
2310
|
+
/* @__PURE__ */ jsxs6("div", { style: styles.cameraContainer, children: [
|
|
2311
|
+
/* @__PURE__ */ jsx7("video", { ref: videoRef, autoPlay: true, playsInline: true, muted: true, style: styles.cameraVideo }),
|
|
2312
|
+
/* @__PURE__ */ jsxs6("div", { style: styles.documentOverlay, children: [
|
|
2313
|
+
/* @__PURE__ */ jsxs6("div", { ref: guideRef, style: styles.documentFrame, children: [
|
|
2314
|
+
/* @__PURE__ */ jsx7("div", { style: { ...styles.corner, top: 0, left: 0 } }),
|
|
2315
|
+
/* @__PURE__ */ jsx7("div", { style: { ...styles.corner, top: 0, right: 0, transform: "rotate(90deg)" } }),
|
|
2316
|
+
/* @__PURE__ */ jsx7("div", { style: { ...styles.corner, bottom: 0, right: 0, transform: "rotate(180deg)" } }),
|
|
2317
|
+
/* @__PURE__ */ jsx7("div", { style: { ...styles.corner, bottom: 0, left: 0, transform: "rotate(270deg)" } }),
|
|
2318
|
+
/* @__PURE__ */ jsx7("div", { style: styles.scanLine })
|
|
2319
|
+
] }),
|
|
2320
|
+
documentSignals.detectorActive && /* @__PURE__ */ jsx7(
|
|
2321
|
+
"div",
|
|
2322
|
+
{
|
|
2323
|
+
style: {
|
|
2324
|
+
position: "absolute",
|
|
2325
|
+
bottom: "24px",
|
|
2326
|
+
left: "50%",
|
|
2327
|
+
transform: "translateX(-50%)",
|
|
2328
|
+
padding: "6px 14px",
|
|
2329
|
+
borderRadius: "999px",
|
|
2330
|
+
fontSize: "12px",
|
|
2331
|
+
fontWeight: 600,
|
|
2332
|
+
color: documentSignals.documentDetected ? colors.success : "rgba(255,255,255,0.55)",
|
|
2333
|
+
backgroundColor: documentSignals.documentDetected ? "rgba(16,185,129,0.15)" : "rgba(0,0,0,0.35)",
|
|
2334
|
+
border: documentSignals.documentDetected ? "1px solid rgba(16,185,129,0.4)" : "1px solid rgba(255,255,255,0.12)",
|
|
2335
|
+
transition: "all 200ms",
|
|
2336
|
+
pointerEvents: "none",
|
|
2337
|
+
whiteSpace: "nowrap"
|
|
2338
|
+
},
|
|
2339
|
+
children: documentSignals.documentDetected ? "\u2713 Document detected \u2014 fill the frame" : "Position your ID inside the guide"
|
|
2340
|
+
}
|
|
2341
|
+
)
|
|
2342
|
+
] }),
|
|
2343
|
+
/* @__PURE__ */ jsx7("canvas", { ref: canvasRef, style: { display: "none" } })
|
|
2006
2344
|
] }),
|
|
2007
|
-
/* @__PURE__ */
|
|
2008
|
-
/* @__PURE__ */
|
|
2345
|
+
/* @__PURE__ */ jsxs6("div", { style: styles.stepPillsRow, children: [
|
|
2346
|
+
/* @__PURE__ */ jsx7(
|
|
2009
2347
|
"span",
|
|
2010
2348
|
{
|
|
2011
2349
|
style: {
|
|
@@ -2016,7 +2354,7 @@ function DocumentCaptureScreen({
|
|
|
2016
2354
|
children: "Front"
|
|
2017
2355
|
}
|
|
2018
2356
|
),
|
|
2019
|
-
requiresBack && /* @__PURE__ */
|
|
2357
|
+
requiresBack && /* @__PURE__ */ jsx7(
|
|
2020
2358
|
"span",
|
|
2021
2359
|
{
|
|
2022
2360
|
style: {
|
|
@@ -2028,7 +2366,7 @@ function DocumentCaptureScreen({
|
|
|
2028
2366
|
}
|
|
2029
2367
|
)
|
|
2030
2368
|
] }),
|
|
2031
|
-
/* @__PURE__ */
|
|
2369
|
+
/* @__PURE__ */ jsx7("div", { style: { textAlign: "center", padding: "8px 0" }, children: /* @__PURE__ */ jsxs6(
|
|
2032
2370
|
"span",
|
|
2033
2371
|
{
|
|
2034
2372
|
style: {
|
|
@@ -2037,44 +2375,44 @@ function DocumentCaptureScreen({
|
|
|
2037
2375
|
color: colors.teal
|
|
2038
2376
|
},
|
|
2039
2377
|
children: [
|
|
2040
|
-
/* @__PURE__ */
|
|
2378
|
+
/* @__PURE__ */ jsx7("span", { style: { ...styles.pulsingDot, backgroundColor: colors.teal } }),
|
|
2041
2379
|
"Scanning document..."
|
|
2042
2380
|
]
|
|
2043
2381
|
}
|
|
2044
2382
|
) }),
|
|
2045
|
-
/* @__PURE__ */
|
|
2383
|
+
/* @__PURE__ */ jsx7("div", { style: styles.captureFooter, children: /* @__PURE__ */ jsx7(
|
|
2046
2384
|
"button",
|
|
2047
2385
|
{
|
|
2048
2386
|
style: { ...styles.captureButton, opacity: isCapturing ? 0.5 : 1 },
|
|
2049
2387
|
onClick: handleCapture,
|
|
2050
2388
|
disabled: isCapturing,
|
|
2051
|
-
children: /* @__PURE__ */
|
|
2389
|
+
children: /* @__PURE__ */ jsx7("div", { style: styles.captureButtonInner })
|
|
2052
2390
|
}
|
|
2053
2391
|
) })
|
|
2054
2392
|
] });
|
|
2055
2393
|
}
|
|
2056
2394
|
function QualityCheck({ label }) {
|
|
2057
|
-
return /* @__PURE__ */
|
|
2058
|
-
/* @__PURE__ */
|
|
2059
|
-
/* @__PURE__ */
|
|
2395
|
+
return /* @__PURE__ */ jsxs6("div", { style: styles.qualityCheck, children: [
|
|
2396
|
+
/* @__PURE__ */ jsx7("div", { style: styles.qualityCheckIcon, children: "\u2713" }),
|
|
2397
|
+
/* @__PURE__ */ jsx7("span", { style: styles.qualityCheckLabel, children: label })
|
|
2060
2398
|
] });
|
|
2061
2399
|
}
|
|
2062
2400
|
|
|
2063
2401
|
// src/components/FlipDocumentScreen.tsx
|
|
2064
|
-
import { useEffect as
|
|
2065
|
-
import { jsx as
|
|
2402
|
+
import { useEffect as useEffect6 } from "react";
|
|
2403
|
+
import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
2066
2404
|
function FlipDocumentScreen({ onContinue, onCancel }) {
|
|
2067
|
-
|
|
2405
|
+
useEffect6(() => {
|
|
2068
2406
|
injectKeyframes();
|
|
2069
2407
|
}, []);
|
|
2070
|
-
return /* @__PURE__ */
|
|
2071
|
-
/* @__PURE__ */
|
|
2072
|
-
/* @__PURE__ */
|
|
2073
|
-
/* @__PURE__ */
|
|
2074
|
-
/* @__PURE__ */
|
|
2075
|
-
/* @__PURE__ */
|
|
2408
|
+
return /* @__PURE__ */ jsxs7("div", { style: styles.darkContainer, children: [
|
|
2409
|
+
/* @__PURE__ */ jsx8(StepProgressBar, { total: 5, current: 3, isDark: true }),
|
|
2410
|
+
/* @__PURE__ */ jsxs7("div", { style: styles.darkScreenHeader, children: [
|
|
2411
|
+
/* @__PURE__ */ jsx8("div", { style: { width: 40 } }),
|
|
2412
|
+
/* @__PURE__ */ jsx8("h1", { style: styles.darkScreenTitle, children: "Flip your document" }),
|
|
2413
|
+
/* @__PURE__ */ jsx8("button", { style: styles.glassCloseButton, onClick: onCancel, children: "\u2715" })
|
|
2076
2414
|
] }),
|
|
2077
|
-
/* @__PURE__ */
|
|
2415
|
+
/* @__PURE__ */ jsxs7("div", { style: {
|
|
2078
2416
|
flex: 1,
|
|
2079
2417
|
display: "flex",
|
|
2080
2418
|
flexDirection: "column",
|
|
@@ -2083,7 +2421,7 @@ function FlipDocumentScreen({ onContinue, onCancel }) {
|
|
|
2083
2421
|
padding: "24px",
|
|
2084
2422
|
gap: "32px"
|
|
2085
2423
|
}, children: [
|
|
2086
|
-
/* @__PURE__ */
|
|
2424
|
+
/* @__PURE__ */ jsx8("div", { style: {
|
|
2087
2425
|
width: "120px",
|
|
2088
2426
|
height: "120px",
|
|
2089
2427
|
borderRadius: "50%",
|
|
@@ -2091,18 +2429,18 @@ function FlipDocumentScreen({ onContinue, onCancel }) {
|
|
|
2091
2429
|
display: "flex",
|
|
2092
2430
|
alignItems: "center",
|
|
2093
2431
|
justifyContent: "center"
|
|
2094
|
-
}, children: /* @__PURE__ */
|
|
2095
|
-
/* @__PURE__ */
|
|
2096
|
-
/* @__PURE__ */
|
|
2432
|
+
}, children: /* @__PURE__ */ jsxs7("svg", { width: "56", height: "56", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
|
|
2433
|
+
/* @__PURE__ */ jsx8("path", { d: "M9 3L5 6.99H8V14H10V6.99H13L9 3Z", fill: colors.teal }),
|
|
2434
|
+
/* @__PURE__ */ jsx8("path", { d: "M16 17.01V10H14V17.01H11L15 21L19 17.01H16Z", fill: colors.teal })
|
|
2097
2435
|
] }) }),
|
|
2098
|
-
/* @__PURE__ */
|
|
2099
|
-
/* @__PURE__ */
|
|
2436
|
+
/* @__PURE__ */ jsxs7("div", { style: { textAlign: "center" }, children: [
|
|
2437
|
+
/* @__PURE__ */ jsx8("h2", { style: {
|
|
2100
2438
|
fontSize: "22px",
|
|
2101
2439
|
fontWeight: 700,
|
|
2102
2440
|
color: colors.white,
|
|
2103
2441
|
margin: "0 0 12px 0"
|
|
2104
2442
|
}, children: "Now capture the back" }),
|
|
2105
|
-
/* @__PURE__ */
|
|
2443
|
+
/* @__PURE__ */ jsx8("p", { style: {
|
|
2106
2444
|
fontSize: "15px",
|
|
2107
2445
|
color: "rgba(255,255,255,0.6)",
|
|
2108
2446
|
margin: 0,
|
|
@@ -2110,38 +2448,38 @@ function FlipDocumentScreen({ onContinue, onCancel }) {
|
|
|
2110
2448
|
maxWidth: "280px"
|
|
2111
2449
|
}, children: "Turn your document over to the back side, then tap continue to take a photo." })
|
|
2112
2450
|
] }),
|
|
2113
|
-
/* @__PURE__ */
|
|
2114
|
-
/* @__PURE__ */
|
|
2451
|
+
/* @__PURE__ */ jsxs7("div", { style: styles.stepPillsRow, children: [
|
|
2452
|
+
/* @__PURE__ */ jsx8("span", { style: {
|
|
2115
2453
|
...styles.stepPill,
|
|
2116
2454
|
backgroundColor: "rgba(16,185,129,0.15)",
|
|
2117
2455
|
color: colors.success
|
|
2118
2456
|
}, children: "\u2713 Front" }),
|
|
2119
|
-
/* @__PURE__ */
|
|
2457
|
+
/* @__PURE__ */ jsx8("span", { style: {
|
|
2120
2458
|
...styles.stepPill,
|
|
2121
2459
|
backgroundColor: colors.teal,
|
|
2122
2460
|
color: colors.white
|
|
2123
2461
|
}, children: "Back" })
|
|
2124
2462
|
] })
|
|
2125
2463
|
] }),
|
|
2126
|
-
/* @__PURE__ */
|
|
2464
|
+
/* @__PURE__ */ jsx8("div", { style: { padding: "24px" }, children: /* @__PURE__ */ jsx8("button", { style: styles.primaryButton, onClick: onContinue, children: "Continue" }) })
|
|
2127
2465
|
] });
|
|
2128
2466
|
}
|
|
2129
2467
|
|
|
2130
2468
|
// src/components/SelfieCaptureScreen.tsx
|
|
2131
|
-
import { useRef as
|
|
2132
|
-
import { jsx as
|
|
2133
|
-
function SelfieCaptureScreen({ onCapture, onCancel }) {
|
|
2134
|
-
const videoRef =
|
|
2135
|
-
const canvasRef =
|
|
2136
|
-
const [stream, setStream] =
|
|
2137
|
-
const [isCapturing, setIsCapturing] =
|
|
2138
|
-
const [error, setError] =
|
|
2139
|
-
const [capturedImage, setCapturedImage] =
|
|
2140
|
-
const [capturedBlob, setCapturedBlob] =
|
|
2141
|
-
|
|
2469
|
+
import { useRef as useRef4, useEffect as useEffect7, useState as useState6, useCallback as useCallback3 } from "react";
|
|
2470
|
+
import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
2471
|
+
function SelfieCaptureScreen({ onCapture, onCancel, showVisualGuides = true }) {
|
|
2472
|
+
const videoRef = useRef4(null);
|
|
2473
|
+
const canvasRef = useRef4(null);
|
|
2474
|
+
const [stream, setStream] = useState6(null);
|
|
2475
|
+
const [isCapturing, setIsCapturing] = useState6(false);
|
|
2476
|
+
const [error, setError] = useState6(null);
|
|
2477
|
+
const [capturedImage, setCapturedImage] = useState6(null);
|
|
2478
|
+
const [capturedBlob, setCapturedBlob] = useState6(null);
|
|
2479
|
+
useEffect7(() => {
|
|
2142
2480
|
injectKeyframes();
|
|
2143
2481
|
}, []);
|
|
2144
|
-
|
|
2482
|
+
useEffect7(() => {
|
|
2145
2483
|
let mounted = true;
|
|
2146
2484
|
async function startCamera() {
|
|
2147
2485
|
try {
|
|
@@ -2163,7 +2501,7 @@ function SelfieCaptureScreen({ onCapture, onCancel }) {
|
|
|
2163
2501
|
mounted = false;
|
|
2164
2502
|
};
|
|
2165
2503
|
}, [capturedImage]);
|
|
2166
|
-
|
|
2504
|
+
useEffect7(() => {
|
|
2167
2505
|
return () => {
|
|
2168
2506
|
stream?.getTracks().forEach((t) => t.stop());
|
|
2169
2507
|
};
|
|
@@ -2207,22 +2545,22 @@ function SelfieCaptureScreen({ onCapture, onCancel }) {
|
|
|
2207
2545
|
}
|
|
2208
2546
|
};
|
|
2209
2547
|
if (error) {
|
|
2210
|
-
return /* @__PURE__ */
|
|
2211
|
-
/* @__PURE__ */
|
|
2212
|
-
/* @__PURE__ */
|
|
2548
|
+
return /* @__PURE__ */ jsx9("div", { style: styles.container, children: /* @__PURE__ */ jsxs8("div", { style: styles.errorContainer, children: [
|
|
2549
|
+
/* @__PURE__ */ jsx9("p", { style: styles.errorText, children: error }),
|
|
2550
|
+
/* @__PURE__ */ jsx9("button", { style: styles.primaryButton, onClick: onCancel, children: "Go Back" })
|
|
2213
2551
|
] }) });
|
|
2214
2552
|
}
|
|
2215
2553
|
if (capturedImage) {
|
|
2216
|
-
return /* @__PURE__ */
|
|
2217
|
-
/* @__PURE__ */
|
|
2218
|
-
/* @__PURE__ */
|
|
2219
|
-
/* @__PURE__ */
|
|
2220
|
-
/* @__PURE__ */
|
|
2221
|
-
/* @__PURE__ */
|
|
2554
|
+
return /* @__PURE__ */ jsxs8("div", { style: styles.darkContainer, children: [
|
|
2555
|
+
/* @__PURE__ */ jsx9(StepProgressBar, { total: 5, current: 4, isDark: true }),
|
|
2556
|
+
/* @__PURE__ */ jsxs8("div", { style: styles.darkScreenHeader, children: [
|
|
2557
|
+
/* @__PURE__ */ jsx9("div", { style: { width: 40 } }),
|
|
2558
|
+
/* @__PURE__ */ jsx9("h1", { style: styles.darkScreenTitle, children: "Does this look like you?" }),
|
|
2559
|
+
/* @__PURE__ */ jsx9("button", { style: styles.glassCloseButton, onClick: onCancel, children: "\u2715" })
|
|
2222
2560
|
] }),
|
|
2223
|
-
/* @__PURE__ */
|
|
2224
|
-
/* @__PURE__ */
|
|
2225
|
-
/* @__PURE__ */
|
|
2561
|
+
/* @__PURE__ */ jsx9("p", { style: styles.darkScreenSubtitle, children: "Check clarity and lighting" }),
|
|
2562
|
+
/* @__PURE__ */ jsx9("div", { style: { flex: 1, display: "flex", alignItems: "center", justifyContent: "center", padding: "24px" }, children: /* @__PURE__ */ jsxs8("div", { style: { position: "relative" }, children: [
|
|
2563
|
+
/* @__PURE__ */ jsx9("div", { style: { width: "240px", height: "300px", borderRadius: "50%", overflow: "hidden", border: `3px solid ${colors.teal}` }, children: /* @__PURE__ */ jsx9(
|
|
2226
2564
|
"img",
|
|
2227
2565
|
{
|
|
2228
2566
|
src: capturedImage,
|
|
@@ -2230,31 +2568,32 @@ function SelfieCaptureScreen({ onCapture, onCancel }) {
|
|
|
2230
2568
|
style: { width: "100%", height: "100%", objectFit: "cover" }
|
|
2231
2569
|
}
|
|
2232
2570
|
) }),
|
|
2233
|
-
/* @__PURE__ */
|
|
2234
|
-
/* @__PURE__ */
|
|
2235
|
-
/* @__PURE__ */
|
|
2236
|
-
/* @__PURE__ */
|
|
2237
|
-
/* @__PURE__ */
|
|
2571
|
+
/* @__PURE__ */ jsx9("div", { style: { textAlign: "center", marginTop: "16px" }, children: /* @__PURE__ */ jsx9("span", { style: styles.reviewBadge, children: "\u2713 Face detected" }) }),
|
|
2572
|
+
/* @__PURE__ */ jsxs8("div", { style: styles.qualityChecks, children: [
|
|
2573
|
+
/* @__PURE__ */ jsx9(QualityCheck2, { label: "Clear" }),
|
|
2574
|
+
/* @__PURE__ */ jsx9(QualityCheck2, { label: "Centered" }),
|
|
2575
|
+
/* @__PURE__ */ jsx9(QualityCheck2, { label: "Well-lit" })
|
|
2238
2576
|
] })
|
|
2239
2577
|
] }) }),
|
|
2240
|
-
/* @__PURE__ */
|
|
2241
|
-
/* @__PURE__ */
|
|
2242
|
-
/* @__PURE__ */
|
|
2578
|
+
/* @__PURE__ */ jsxs8("div", { style: styles.reviewButtonsRow, children: [
|
|
2579
|
+
/* @__PURE__ */ jsx9("button", { style: { ...styles.darkOutlineButton, flex: 1 }, onClick: handleRetake, children: "Retake" }),
|
|
2580
|
+
/* @__PURE__ */ jsx9("button", { style: { ...styles.primaryButton, flex: 1 }, onClick: handleAccept, children: "Use this" })
|
|
2243
2581
|
] })
|
|
2244
2582
|
] });
|
|
2245
2583
|
}
|
|
2246
|
-
return /* @__PURE__ */
|
|
2247
|
-
/* @__PURE__ */
|
|
2248
|
-
/* @__PURE__ */
|
|
2249
|
-
/* @__PURE__ */
|
|
2250
|
-
/* @__PURE__ */
|
|
2251
|
-
/* @__PURE__ */
|
|
2252
|
-
/* @__PURE__ */
|
|
2584
|
+
return /* @__PURE__ */ jsxs8("div", { style: styles.captureContainer, children: [
|
|
2585
|
+
/* @__PURE__ */ jsx9(StepProgressBar, { total: 5, current: 4, isDark: true }),
|
|
2586
|
+
/* @__PURE__ */ jsxs8("div", { style: styles.darkScreenHeader, children: [
|
|
2587
|
+
/* @__PURE__ */ jsx9("div", { style: { width: 40 } }),
|
|
2588
|
+
/* @__PURE__ */ jsxs8("div", { style: { flex: 1, textAlign: "center" }, children: [
|
|
2589
|
+
/* @__PURE__ */ jsx9("h1", { style: { ...styles.darkScreenTitle, margin: 0, fontSize: "24px", fontWeight: 700 }, children: "Face the camera" }),
|
|
2590
|
+
/* @__PURE__ */ jsx9("p", { style: styles.darkScreenSubtitle, children: "Keep a neutral expression" })
|
|
2253
2591
|
] }),
|
|
2254
|
-
/* @__PURE__ */
|
|
2592
|
+
/* @__PURE__ */ jsx9("button", { style: styles.glassCloseButton, onClick: onCancel, children: "\u2715" })
|
|
2255
2593
|
] }),
|
|
2256
|
-
/* @__PURE__ */
|
|
2257
|
-
|
|
2594
|
+
showVisualGuides && /* @__PURE__ */ jsx9("div", { style: { display: "flex", justifyContent: "center", padding: "6px 0 0" }, children: /* @__PURE__ */ jsx9(VisualGuide, { kind: "selfie", size: 56 }) }),
|
|
2595
|
+
/* @__PURE__ */ jsxs8("div", { style: styles.cameraContainer, children: [
|
|
2596
|
+
/* @__PURE__ */ jsx9(
|
|
2258
2597
|
"video",
|
|
2259
2598
|
{
|
|
2260
2599
|
ref: videoRef,
|
|
@@ -2264,10 +2603,10 @@ function SelfieCaptureScreen({ onCapture, onCancel }) {
|
|
|
2264
2603
|
style: { ...styles.cameraVideo, transform: "scaleX(-1)" }
|
|
2265
2604
|
}
|
|
2266
2605
|
),
|
|
2267
|
-
/* @__PURE__ */
|
|
2268
|
-
/* @__PURE__ */
|
|
2606
|
+
/* @__PURE__ */ jsx9("div", { style: styles.selfieOverlay, children: /* @__PURE__ */ jsx9("div", { style: styles.faceGuide, children: /* @__PURE__ */ jsx9("div", { style: styles.rotatingRing }) }) }),
|
|
2607
|
+
/* @__PURE__ */ jsx9("canvas", { ref: canvasRef, style: { display: "none" } })
|
|
2269
2608
|
] }),
|
|
2270
|
-
/* @__PURE__ */
|
|
2609
|
+
/* @__PURE__ */ jsx9("div", { style: { textAlign: "center", padding: "8px 0" }, children: /* @__PURE__ */ jsxs8(
|
|
2271
2610
|
"span",
|
|
2272
2611
|
{
|
|
2273
2612
|
style: {
|
|
@@ -2276,32 +2615,88 @@ function SelfieCaptureScreen({ onCapture, onCancel }) {
|
|
|
2276
2615
|
color: colors.teal
|
|
2277
2616
|
},
|
|
2278
2617
|
children: [
|
|
2279
|
-
/* @__PURE__ */
|
|
2618
|
+
/* @__PURE__ */ jsx9("span", { style: { ...styles.pulsingDot, backgroundColor: colors.teal } }),
|
|
2280
2619
|
"Position your face in the oval"
|
|
2281
2620
|
]
|
|
2282
2621
|
}
|
|
2283
2622
|
) }),
|
|
2284
|
-
/* @__PURE__ */
|
|
2623
|
+
/* @__PURE__ */ jsx9("div", { style: styles.captureFooter, children: /* @__PURE__ */ jsx9(
|
|
2285
2624
|
"button",
|
|
2286
2625
|
{
|
|
2287
2626
|
style: { ...styles.captureButton, opacity: isCapturing ? 0.5 : 1 },
|
|
2288
2627
|
onClick: handleCapture,
|
|
2289
2628
|
disabled: isCapturing,
|
|
2290
|
-
children: /* @__PURE__ */
|
|
2629
|
+
children: /* @__PURE__ */ jsx9("div", { style: styles.captureButtonInner })
|
|
2291
2630
|
}
|
|
2292
2631
|
) })
|
|
2293
2632
|
] });
|
|
2294
2633
|
}
|
|
2295
2634
|
function QualityCheck2({ label }) {
|
|
2296
|
-
return /* @__PURE__ */
|
|
2297
|
-
/* @__PURE__ */
|
|
2298
|
-
/* @__PURE__ */
|
|
2635
|
+
return /* @__PURE__ */ jsxs8("div", { style: styles.qualityCheck, children: [
|
|
2636
|
+
/* @__PURE__ */ jsx9("div", { style: styles.qualityCheckIcon, children: "\u2713" }),
|
|
2637
|
+
/* @__PURE__ */ jsx9("span", { style: styles.qualityCheckLabel, children: label })
|
|
2299
2638
|
] });
|
|
2300
2639
|
}
|
|
2301
2640
|
|
|
2302
2641
|
// src/components/LivenessScreen.tsx
|
|
2303
|
-
import { useEffect as
|
|
2304
|
-
|
|
2642
|
+
import { useEffect as useEffect9, useRef as useRef6, useState as useState8, useCallback as useCallback4 } from "react";
|
|
2643
|
+
|
|
2644
|
+
// src/hooks/useLivenessSignals.ts
|
|
2645
|
+
import { useEffect as useEffect8, useRef as useRef5, useState as useState7 } from "react";
|
|
2646
|
+
var FRAME_INTERVAL_MS2 = 250;
|
|
2647
|
+
function useLivenessSignals(videoRef) {
|
|
2648
|
+
const [signals, setSignals] = useState7({
|
|
2649
|
+
faceDetected: false,
|
|
2650
|
+
detectorActive: false
|
|
2651
|
+
});
|
|
2652
|
+
const detectorRef = useRef5(null);
|
|
2653
|
+
const intervalRef = useRef5(null);
|
|
2654
|
+
useEffect8(() => {
|
|
2655
|
+
let cancelled = false;
|
|
2656
|
+
async function setupDetector() {
|
|
2657
|
+
const NativeFaceDetector = typeof window !== "undefined" && "FaceDetector" in window ? window.FaceDetector : null;
|
|
2658
|
+
if (!NativeFaceDetector) {
|
|
2659
|
+
return;
|
|
2660
|
+
}
|
|
2661
|
+
try {
|
|
2662
|
+
const detector = new NativeFaceDetector({ fastMode: true, maxDetectedFaces: 1 });
|
|
2663
|
+
if (cancelled) return;
|
|
2664
|
+
detectorRef.current = detector;
|
|
2665
|
+
setSignals((prev) => ({ ...prev, detectorActive: true }));
|
|
2666
|
+
} catch {
|
|
2667
|
+
return;
|
|
2668
|
+
}
|
|
2669
|
+
intervalRef.current = setInterval(async () => {
|
|
2670
|
+
const detector = detectorRef.current;
|
|
2671
|
+
const video = videoRef.current;
|
|
2672
|
+
if (!detector || !video || video.readyState < 2) return;
|
|
2673
|
+
try {
|
|
2674
|
+
const faces = await detector.detect(video);
|
|
2675
|
+
if (cancelled) return;
|
|
2676
|
+
setSignals((prev) => {
|
|
2677
|
+
const next = faces.length > 0;
|
|
2678
|
+
if (prev.faceDetected === next) return prev;
|
|
2679
|
+
return { ...prev, faceDetected: next };
|
|
2680
|
+
});
|
|
2681
|
+
} catch {
|
|
2682
|
+
}
|
|
2683
|
+
}, FRAME_INTERVAL_MS2);
|
|
2684
|
+
}
|
|
2685
|
+
setupDetector();
|
|
2686
|
+
return () => {
|
|
2687
|
+
cancelled = true;
|
|
2688
|
+
if (intervalRef.current) {
|
|
2689
|
+
clearInterval(intervalRef.current);
|
|
2690
|
+
intervalRef.current = null;
|
|
2691
|
+
}
|
|
2692
|
+
detectorRef.current = null;
|
|
2693
|
+
};
|
|
2694
|
+
}, [videoRef]);
|
|
2695
|
+
return signals;
|
|
2696
|
+
}
|
|
2697
|
+
|
|
2698
|
+
// src/components/LivenessScreen.tsx
|
|
2699
|
+
import { jsx as jsx10, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
2305
2700
|
function LivenessScreen({
|
|
2306
2701
|
session,
|
|
2307
2702
|
currentChallenge,
|
|
@@ -2309,22 +2704,25 @@ function LivenessScreen({
|
|
|
2309
2704
|
onChallengeComplete,
|
|
2310
2705
|
onStart,
|
|
2311
2706
|
onComplete,
|
|
2312
|
-
onCancel
|
|
2707
|
+
onCancel,
|
|
2708
|
+
lastChallengeError,
|
|
2709
|
+
showVisualGuides = true
|
|
2313
2710
|
}) {
|
|
2314
|
-
const videoRef =
|
|
2315
|
-
const canvasRef =
|
|
2316
|
-
const [stream, setStream] =
|
|
2317
|
-
const
|
|
2318
|
-
const [
|
|
2319
|
-
const [
|
|
2320
|
-
const [
|
|
2321
|
-
|
|
2711
|
+
const videoRef = useRef6(null);
|
|
2712
|
+
const canvasRef = useRef6(null);
|
|
2713
|
+
const [stream, setStream] = useState8(null);
|
|
2714
|
+
const livenessSignals = useLivenessSignals(videoRef);
|
|
2715
|
+
const [cameraError, setCameraError] = useState8(null);
|
|
2716
|
+
const [phase, setPhase] = useState8("preparing");
|
|
2717
|
+
const [countdown, setCountdown] = useState8(3);
|
|
2718
|
+
const [capturing, setCapturing] = useState8(false);
|
|
2719
|
+
useEffect9(() => {
|
|
2322
2720
|
injectKeyframes();
|
|
2323
2721
|
}, []);
|
|
2324
|
-
|
|
2722
|
+
useEffect9(() => {
|
|
2325
2723
|
if (!session) onStart();
|
|
2326
2724
|
}, [session, onStart]);
|
|
2327
|
-
|
|
2725
|
+
useEffect9(() => {
|
|
2328
2726
|
let mounted = true;
|
|
2329
2727
|
async function startCamera() {
|
|
2330
2728
|
try {
|
|
@@ -2356,12 +2754,12 @@ function LivenessScreen({
|
|
|
2356
2754
|
mounted = false;
|
|
2357
2755
|
};
|
|
2358
2756
|
}, []);
|
|
2359
|
-
|
|
2757
|
+
useEffect9(() => {
|
|
2360
2758
|
return () => {
|
|
2361
2759
|
stream?.getTracks().forEach((t) => t.stop());
|
|
2362
2760
|
};
|
|
2363
2761
|
}, [stream]);
|
|
2364
|
-
|
|
2762
|
+
useEffect9(() => {
|
|
2365
2763
|
if (!currentChallenge) return;
|
|
2366
2764
|
setPhase("preparing");
|
|
2367
2765
|
setCountdown(3);
|
|
@@ -2389,7 +2787,7 @@ function LivenessScreen({
|
|
|
2389
2787
|
0.85
|
|
2390
2788
|
);
|
|
2391
2789
|
}, [currentChallenge, capturing, onChallengeComplete]);
|
|
2392
|
-
|
|
2790
|
+
useEffect9(() => {
|
|
2393
2791
|
if (!currentChallenge || capturing) return;
|
|
2394
2792
|
if (countdown === 0) {
|
|
2395
2793
|
if (phase === "preparing") {
|
|
@@ -2403,32 +2801,32 @@ function LivenessScreen({
|
|
|
2403
2801
|
const t = setTimeout(() => setCountdown((c) => c - 1), 1e3);
|
|
2404
2802
|
return () => clearTimeout(t);
|
|
2405
2803
|
}, [countdown, currentChallenge?.id, capturing, captureFrame, phase]);
|
|
2406
|
-
|
|
2804
|
+
useEffect9(() => {
|
|
2407
2805
|
if (session && !currentChallenge && completedChallenges > 0) {
|
|
2408
2806
|
onComplete();
|
|
2409
2807
|
}
|
|
2410
2808
|
}, [session, currentChallenge, completedChallenges, onComplete]);
|
|
2411
2809
|
if (cameraError) {
|
|
2412
|
-
return /* @__PURE__ */
|
|
2413
|
-
/* @__PURE__ */
|
|
2414
|
-
/* @__PURE__ */
|
|
2810
|
+
return /* @__PURE__ */ jsx10("div", { style: styles.darkContainer, children: /* @__PURE__ */ jsxs9("div", { style: styles.errorContainer, children: [
|
|
2811
|
+
/* @__PURE__ */ jsx10("p", { style: styles.errorText, children: cameraError }),
|
|
2812
|
+
/* @__PURE__ */ jsx10("button", { style: styles.primaryButton, onClick: onCancel, children: "Go back" })
|
|
2415
2813
|
] }) });
|
|
2416
2814
|
}
|
|
2417
2815
|
if (!session) {
|
|
2418
|
-
return /* @__PURE__ */
|
|
2419
|
-
/* @__PURE__ */
|
|
2420
|
-
/* @__PURE__ */
|
|
2816
|
+
return /* @__PURE__ */ jsx10("div", { style: styles.darkContainer, children: /* @__PURE__ */ jsxs9("div", { style: styles.loadingContainer, children: [
|
|
2817
|
+
/* @__PURE__ */ jsx10("div", { style: styles.spinner }),
|
|
2818
|
+
/* @__PURE__ */ jsx10("p", { style: { ...styles.loadingText, color: "rgba(255,255,255,0.6)" }, children: "Starting liveness check..." })
|
|
2421
2819
|
] }) });
|
|
2422
2820
|
}
|
|
2423
2821
|
const totalChallenges = session.challenges.length;
|
|
2424
|
-
return /* @__PURE__ */
|
|
2425
|
-
/* @__PURE__ */
|
|
2426
|
-
/* @__PURE__ */
|
|
2427
|
-
/* @__PURE__ */
|
|
2428
|
-
/* @__PURE__ */
|
|
2429
|
-
/* @__PURE__ */
|
|
2822
|
+
return /* @__PURE__ */ jsxs9("div", { style: styles.captureContainer, children: [
|
|
2823
|
+
/* @__PURE__ */ jsx10(StepProgressBar, { total: 5, current: 5, isDark: true }),
|
|
2824
|
+
/* @__PURE__ */ jsxs9("div", { style: styles.darkScreenHeader, children: [
|
|
2825
|
+
/* @__PURE__ */ jsx10("div", { style: { width: 40 } }),
|
|
2826
|
+
/* @__PURE__ */ jsx10("h1", { style: styles.darkScreenTitle, children: "Liveness Check" }),
|
|
2827
|
+
/* @__PURE__ */ jsx10("button", { style: styles.glassCloseButton, onClick: onCancel, children: "\u2715" })
|
|
2430
2828
|
] }),
|
|
2431
|
-
/* @__PURE__ */
|
|
2829
|
+
/* @__PURE__ */ jsxs9(
|
|
2432
2830
|
"div",
|
|
2433
2831
|
{
|
|
2434
2832
|
style: {
|
|
@@ -2441,8 +2839,8 @@ function LivenessScreen({
|
|
|
2441
2839
|
padding: "16px 0"
|
|
2442
2840
|
},
|
|
2443
2841
|
children: [
|
|
2444
|
-
/* @__PURE__ */
|
|
2445
|
-
/* @__PURE__ */
|
|
2842
|
+
/* @__PURE__ */ jsxs9("div", { style: { position: "relative" }, children: [
|
|
2843
|
+
/* @__PURE__ */ jsx10(
|
|
2446
2844
|
"div",
|
|
2447
2845
|
{
|
|
2448
2846
|
style: {
|
|
@@ -2453,7 +2851,7 @@ function LivenessScreen({
|
|
|
2453
2851
|
backgroundColor: "#000",
|
|
2454
2852
|
border: "3px solid rgba(255,255,255,0.2)"
|
|
2455
2853
|
},
|
|
2456
|
-
children: /* @__PURE__ */
|
|
2854
|
+
children: /* @__PURE__ */ jsx10(
|
|
2457
2855
|
"video",
|
|
2458
2856
|
{
|
|
2459
2857
|
ref: videoRef,
|
|
@@ -2470,7 +2868,7 @@ function LivenessScreen({
|
|
|
2470
2868
|
)
|
|
2471
2869
|
}
|
|
2472
2870
|
),
|
|
2473
|
-
/* @__PURE__ */
|
|
2871
|
+
/* @__PURE__ */ jsx10(
|
|
2474
2872
|
"svg",
|
|
2475
2873
|
{
|
|
2476
2874
|
style: {
|
|
@@ -2482,7 +2880,7 @@ function LivenessScreen({
|
|
|
2482
2880
|
width: "256",
|
|
2483
2881
|
height: "316",
|
|
2484
2882
|
viewBox: "0 0 256 316",
|
|
2485
|
-
children: /* @__PURE__ */
|
|
2883
|
+
children: /* @__PURE__ */ jsx10(
|
|
2486
2884
|
"ellipse",
|
|
2487
2885
|
{
|
|
2488
2886
|
cx: "128",
|
|
@@ -2498,9 +2896,30 @@ function LivenessScreen({
|
|
|
2498
2896
|
}
|
|
2499
2897
|
)
|
|
2500
2898
|
}
|
|
2899
|
+
),
|
|
2900
|
+
livenessSignals.detectorActive && /* @__PURE__ */ jsx10(
|
|
2901
|
+
"div",
|
|
2902
|
+
{
|
|
2903
|
+
style: {
|
|
2904
|
+
position: "absolute",
|
|
2905
|
+
bottom: "-32px",
|
|
2906
|
+
left: "50%",
|
|
2907
|
+
transform: "translateX(-50%)",
|
|
2908
|
+
padding: "6px 14px",
|
|
2909
|
+
borderRadius: "999px",
|
|
2910
|
+
fontSize: "12px",
|
|
2911
|
+
fontWeight: 600,
|
|
2912
|
+
color: livenessSignals.faceDetected ? colors.success : "rgba(255,255,255,0.55)",
|
|
2913
|
+
backgroundColor: livenessSignals.faceDetected ? "rgba(16,185,129,0.15)" : "rgba(255,255,255,0.06)",
|
|
2914
|
+
border: livenessSignals.faceDetected ? "1px solid rgba(16,185,129,0.4)" : "1px solid rgba(255,255,255,0.12)",
|
|
2915
|
+
transition: "all 200ms",
|
|
2916
|
+
whiteSpace: "nowrap"
|
|
2917
|
+
},
|
|
2918
|
+
children: livenessSignals.faceDetected ? "\u2713 Face detected" : "Position your face in the oval"
|
|
2919
|
+
}
|
|
2501
2920
|
)
|
|
2502
2921
|
] }),
|
|
2503
|
-
currentChallenge && /* @__PURE__ */
|
|
2922
|
+
currentChallenge && /* @__PURE__ */ jsxs9(
|
|
2504
2923
|
"div",
|
|
2505
2924
|
{
|
|
2506
2925
|
style: {
|
|
@@ -2513,7 +2932,14 @@ function LivenessScreen({
|
|
|
2513
2932
|
transition: "background-color 200ms, border-color 200ms"
|
|
2514
2933
|
},
|
|
2515
2934
|
children: [
|
|
2516
|
-
/* @__PURE__ */
|
|
2935
|
+
showVisualGuides && visualGuideForChallenge(currentChallenge.type) && /* @__PURE__ */ jsx10("div", { style: { display: "flex", justifyContent: "center", marginBottom: "12px" }, children: /* @__PURE__ */ jsx10(
|
|
2936
|
+
VisualGuide,
|
|
2937
|
+
{
|
|
2938
|
+
kind: visualGuideForChallenge(currentChallenge.type),
|
|
2939
|
+
size: 64
|
|
2940
|
+
}
|
|
2941
|
+
) }),
|
|
2942
|
+
/* @__PURE__ */ jsx10(
|
|
2517
2943
|
"p",
|
|
2518
2944
|
{
|
|
2519
2945
|
style: {
|
|
@@ -2527,7 +2953,7 @@ function LivenessScreen({
|
|
|
2527
2953
|
children: capturing ? "Checking..." : phase === "preparing" ? "Get ready" : "Now \u2014 hold the pose"
|
|
2528
2954
|
}
|
|
2529
2955
|
),
|
|
2530
|
-
/* @__PURE__ */
|
|
2956
|
+
/* @__PURE__ */ jsx10(
|
|
2531
2957
|
"h2",
|
|
2532
2958
|
{
|
|
2533
2959
|
style: {
|
|
@@ -2538,7 +2964,7 @@ function LivenessScreen({
|
|
|
2538
2964
|
children: currentChallenge.instruction
|
|
2539
2965
|
}
|
|
2540
2966
|
),
|
|
2541
|
-
!capturing && /* @__PURE__ */
|
|
2967
|
+
!capturing && /* @__PURE__ */ jsx10(
|
|
2542
2968
|
"p",
|
|
2543
2969
|
{
|
|
2544
2970
|
style: {
|
|
@@ -2550,16 +2976,33 @@ function LivenessScreen({
|
|
|
2550
2976
|
},
|
|
2551
2977
|
children: countdown
|
|
2552
2978
|
}
|
|
2979
|
+
),
|
|
2980
|
+
lastChallengeError && phase === "preparing" && !capturing && /* @__PURE__ */ jsx10(
|
|
2981
|
+
"p",
|
|
2982
|
+
{
|
|
2983
|
+
style: {
|
|
2984
|
+
margin: "14px 0 0",
|
|
2985
|
+
padding: "10px 12px",
|
|
2986
|
+
borderRadius: "10px",
|
|
2987
|
+
fontSize: "13px",
|
|
2988
|
+
fontWeight: 500,
|
|
2989
|
+
color: "#fca5a5",
|
|
2990
|
+
backgroundColor: "rgba(239,68,68,0.10)",
|
|
2991
|
+
border: "1px solid rgba(239,68,68,0.25)",
|
|
2992
|
+
lineHeight: 1.35
|
|
2993
|
+
},
|
|
2994
|
+
children: lastChallengeError
|
|
2995
|
+
}
|
|
2553
2996
|
)
|
|
2554
2997
|
]
|
|
2555
2998
|
}
|
|
2556
2999
|
),
|
|
2557
|
-
/* @__PURE__ */
|
|
3000
|
+
/* @__PURE__ */ jsx10("canvas", { ref: canvasRef, style: { display: "none" } })
|
|
2558
3001
|
]
|
|
2559
3002
|
}
|
|
2560
3003
|
),
|
|
2561
|
-
/* @__PURE__ */
|
|
2562
|
-
/* @__PURE__ */
|
|
3004
|
+
/* @__PURE__ */ jsxs9("div", { style: { padding: "16px 0" }, children: [
|
|
3005
|
+
/* @__PURE__ */ jsx10("div", { style: styles.progressDots, children: session.challenges.map((_, index) => /* @__PURE__ */ jsx10(
|
|
2563
3006
|
"div",
|
|
2564
3007
|
{
|
|
2565
3008
|
style: {
|
|
@@ -2569,7 +3012,7 @@ function LivenessScreen({
|
|
|
2569
3012
|
},
|
|
2570
3013
|
index
|
|
2571
3014
|
)) }),
|
|
2572
|
-
/* @__PURE__ */
|
|
3015
|
+
/* @__PURE__ */ jsxs9("p", { style: styles.progressText, children: [
|
|
2573
3016
|
"Challenge ",
|
|
2574
3017
|
Math.min(completedChallenges + 1, totalChallenges),
|
|
2575
3018
|
" of",
|
|
@@ -2577,7 +3020,7 @@ function LivenessScreen({
|
|
|
2577
3020
|
totalChallenges
|
|
2578
3021
|
] })
|
|
2579
3022
|
] }),
|
|
2580
|
-
/* @__PURE__ */
|
|
3023
|
+
/* @__PURE__ */ jsx10("div", { style: { padding: "0 24px 24px", textAlign: "center" }, children: /* @__PURE__ */ jsx10(
|
|
2581
3024
|
"p",
|
|
2582
3025
|
{
|
|
2583
3026
|
style: {
|
|
@@ -2592,35 +3035,35 @@ function LivenessScreen({
|
|
|
2592
3035
|
}
|
|
2593
3036
|
|
|
2594
3037
|
// src/components/ResultScreen.tsx
|
|
2595
|
-
import { jsx as
|
|
3038
|
+
import { jsx as jsx11, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
2596
3039
|
function ResultScreen({ verification, onDone, onRetry, resultPageMode, simplified, customMessages }) {
|
|
2597
3040
|
const { status } = verification;
|
|
2598
3041
|
const effectiveMode = resultPageMode ?? (simplified ? "simplified" : "detailed");
|
|
2599
3042
|
if (effectiveMode === "simplified") {
|
|
2600
3043
|
switch (status) {
|
|
2601
3044
|
case "approved":
|
|
2602
|
-
return /* @__PURE__ */
|
|
3045
|
+
return /* @__PURE__ */ jsx11(SimplifiedSuccess, { onDone, customMessages });
|
|
2603
3046
|
case "rejected":
|
|
2604
|
-
return /* @__PURE__ */
|
|
3047
|
+
return /* @__PURE__ */ jsx11(SimplifiedFailed, { onRetry: onRetry || onDone, customMessages });
|
|
2605
3048
|
case "review_required":
|
|
2606
|
-
return /* @__PURE__ */
|
|
3049
|
+
return /* @__PURE__ */ jsx11(SimplifiedReview, { verification, onDone, customMessages });
|
|
2607
3050
|
case "expired":
|
|
2608
|
-
return /* @__PURE__ */
|
|
3051
|
+
return /* @__PURE__ */ jsx11(SimplifiedFailed, { onRetry: onRetry || onDone, customMessages: { failedTitle: "Document Expired", failedMessage: "The document you submitted has expired. Please use a valid document." } });
|
|
2609
3052
|
default:
|
|
2610
|
-
return /* @__PURE__ */
|
|
3053
|
+
return /* @__PURE__ */ jsx11(SimplifiedSuccess, { onDone, customMessages });
|
|
2611
3054
|
}
|
|
2612
3055
|
}
|
|
2613
3056
|
switch (status) {
|
|
2614
3057
|
case "approved":
|
|
2615
|
-
return /* @__PURE__ */
|
|
3058
|
+
return /* @__PURE__ */ jsx11(SuccessResult, { verification, onDone });
|
|
2616
3059
|
case "rejected":
|
|
2617
|
-
return /* @__PURE__ */
|
|
3060
|
+
return /* @__PURE__ */ jsx11(RejectedResult, { verification, onRetry: onRetry || onDone });
|
|
2618
3061
|
case "expired":
|
|
2619
|
-
return /* @__PURE__ */
|
|
3062
|
+
return /* @__PURE__ */ jsx11(ExpiredResult, { verification, onRetry: onRetry || onDone });
|
|
2620
3063
|
case "review_required":
|
|
2621
|
-
return /* @__PURE__ */
|
|
3064
|
+
return /* @__PURE__ */ jsx11(ManualReviewResult, { verification, onDone });
|
|
2622
3065
|
default:
|
|
2623
|
-
return /* @__PURE__ */
|
|
3066
|
+
return /* @__PURE__ */ jsx11(SuccessResult, { verification, onDone });
|
|
2624
3067
|
}
|
|
2625
3068
|
}
|
|
2626
3069
|
function SuccessResult({ verification, onDone }) {
|
|
@@ -2628,16 +3071,16 @@ function SuccessResult({ verification, onDone }) {
|
|
|
2628
3071
|
verification.scores?.overall ?? 100 - (verification.riskScore ?? 16)
|
|
2629
3072
|
);
|
|
2630
3073
|
const metrics = computeScoreBreakdown(verification);
|
|
2631
|
-
return /* @__PURE__ */
|
|
2632
|
-
/* @__PURE__ */
|
|
2633
|
-
/* @__PURE__ */
|
|
3074
|
+
return /* @__PURE__ */ jsxs10("div", { style: styles.resultContainer, children: [
|
|
3075
|
+
/* @__PURE__ */ jsxs10("div", { style: styles.resultContent, children: [
|
|
3076
|
+
/* @__PURE__ */ jsx11(
|
|
2634
3077
|
"div",
|
|
2635
3078
|
{
|
|
2636
3079
|
style: {
|
|
2637
3080
|
...styles.resultIconOuterRing,
|
|
2638
3081
|
backgroundColor: `${colors.success}15`
|
|
2639
3082
|
},
|
|
2640
|
-
children: /* @__PURE__ */
|
|
3083
|
+
children: /* @__PURE__ */ jsx11(
|
|
2641
3084
|
"div",
|
|
2642
3085
|
{
|
|
2643
3086
|
style: {
|
|
@@ -2651,9 +3094,9 @@ function SuccessResult({ verification, onDone }) {
|
|
|
2651
3094
|
)
|
|
2652
3095
|
}
|
|
2653
3096
|
),
|
|
2654
|
-
/* @__PURE__ */
|
|
2655
|
-
/* @__PURE__ */
|
|
2656
|
-
/* @__PURE__ */
|
|
3097
|
+
/* @__PURE__ */ jsx11("h1", { style: styles.resultTitle, children: "Verification approved" }),
|
|
3098
|
+
/* @__PURE__ */ jsx11("p", { style: styles.resultSubtitle, children: "Your identity has been successfully verified." }),
|
|
3099
|
+
/* @__PURE__ */ jsx11(
|
|
2657
3100
|
ScoreCard,
|
|
2658
3101
|
{
|
|
2659
3102
|
score,
|
|
@@ -2661,9 +3104,9 @@ function SuccessResult({ verification, onDone }) {
|
|
|
2661
3104
|
gradient: `linear-gradient(135deg, ${colors.teal}, ${colors.tealDark})`
|
|
2662
3105
|
}
|
|
2663
3106
|
),
|
|
2664
|
-
metrics.map((m, i) => /* @__PURE__ */
|
|
3107
|
+
metrics.map((m, i) => /* @__PURE__ */ jsx11(ScoreMetricRow, { ...m }, i))
|
|
2665
3108
|
] }),
|
|
2666
|
-
/* @__PURE__ */
|
|
3109
|
+
/* @__PURE__ */ jsx11("div", { style: styles.footer, children: /* @__PURE__ */ jsx11("button", { style: styles.primaryButton, onClick: onDone, children: "Done" }) })
|
|
2667
3110
|
] });
|
|
2668
3111
|
}
|
|
2669
3112
|
function RejectedResult({ verification, onRetry }) {
|
|
@@ -2671,16 +3114,16 @@ function RejectedResult({ verification, onRetry }) {
|
|
|
2671
3114
|
verification.scores?.overall ?? 100 - (verification.riskScore ?? 58)
|
|
2672
3115
|
);
|
|
2673
3116
|
const metrics = computeScoreBreakdown(verification);
|
|
2674
|
-
return /* @__PURE__ */
|
|
2675
|
-
/* @__PURE__ */
|
|
2676
|
-
/* @__PURE__ */
|
|
3117
|
+
return /* @__PURE__ */ jsxs10("div", { style: styles.resultContainer, children: [
|
|
3118
|
+
/* @__PURE__ */ jsxs10("div", { style: styles.resultContent, children: [
|
|
3119
|
+
/* @__PURE__ */ jsx11(
|
|
2677
3120
|
"div",
|
|
2678
3121
|
{
|
|
2679
3122
|
style: {
|
|
2680
3123
|
...styles.resultIconOuterRing,
|
|
2681
3124
|
backgroundColor: `${colors.error}15`
|
|
2682
3125
|
},
|
|
2683
|
-
children: /* @__PURE__ */
|
|
3126
|
+
children: /* @__PURE__ */ jsx11(
|
|
2684
3127
|
"div",
|
|
2685
3128
|
{
|
|
2686
3129
|
style: {
|
|
@@ -2694,9 +3137,9 @@ function RejectedResult({ verification, onRetry }) {
|
|
|
2694
3137
|
)
|
|
2695
3138
|
}
|
|
2696
3139
|
),
|
|
2697
|
-
/* @__PURE__ */
|
|
2698
|
-
/* @__PURE__ */
|
|
2699
|
-
/* @__PURE__ */
|
|
3140
|
+
/* @__PURE__ */ jsx11("h1", { style: styles.resultTitle, children: "Verification rejected" }),
|
|
3141
|
+
/* @__PURE__ */ jsx11("p", { style: styles.resultSubtitle, children: "We could not verify your identity. Please try again with a valid document." }),
|
|
3142
|
+
/* @__PURE__ */ jsx11(
|
|
2700
3143
|
ScoreCard,
|
|
2701
3144
|
{
|
|
2702
3145
|
score,
|
|
@@ -2704,15 +3147,15 @@ function RejectedResult({ verification, onRetry }) {
|
|
|
2704
3147
|
gradient: `linear-gradient(135deg, ${colors.error}, #B91C1C)`
|
|
2705
3148
|
}
|
|
2706
3149
|
),
|
|
2707
|
-
metrics.map((m, i) => /* @__PURE__ */
|
|
3150
|
+
metrics.map((m, i) => /* @__PURE__ */ jsx11(ScoreMetricRow, { ...m }, i))
|
|
2708
3151
|
] }),
|
|
2709
|
-
/* @__PURE__ */
|
|
3152
|
+
/* @__PURE__ */ jsx11("div", { style: styles.footer, children: /* @__PURE__ */ jsx11("button", { style: styles.primaryButton, onClick: onRetry, children: "Try again" }) })
|
|
2710
3153
|
] });
|
|
2711
3154
|
}
|
|
2712
3155
|
function ExpiredResult({ verification, onRetry }) {
|
|
2713
|
-
return /* @__PURE__ */
|
|
2714
|
-
/* @__PURE__ */
|
|
2715
|
-
/* @__PURE__ */
|
|
3156
|
+
return /* @__PURE__ */ jsxs10("div", { style: styles.resultContainer, children: [
|
|
3157
|
+
/* @__PURE__ */ jsxs10("div", { style: styles.resultContent, children: [
|
|
3158
|
+
/* @__PURE__ */ jsx11(
|
|
2716
3159
|
"div",
|
|
2717
3160
|
{
|
|
2718
3161
|
style: {
|
|
@@ -2720,7 +3163,7 @@ function ExpiredResult({ verification, onRetry }) {
|
|
|
2720
3163
|
backgroundColor: `${colors.warning}15`,
|
|
2721
3164
|
border: `2px solid ${colors.warning}30`
|
|
2722
3165
|
},
|
|
2723
|
-
children: /* @__PURE__ */
|
|
3166
|
+
children: /* @__PURE__ */ jsx11(
|
|
2724
3167
|
"div",
|
|
2725
3168
|
{
|
|
2726
3169
|
style: {
|
|
@@ -2734,29 +3177,29 @@ function ExpiredResult({ verification, onRetry }) {
|
|
|
2734
3177
|
)
|
|
2735
3178
|
}
|
|
2736
3179
|
),
|
|
2737
|
-
/* @__PURE__ */
|
|
2738
|
-
/* @__PURE__ */
|
|
2739
|
-
verification.documentVerification && /* @__PURE__ */
|
|
2740
|
-
/* @__PURE__ */
|
|
2741
|
-
/* @__PURE__ */
|
|
2742
|
-
/* @__PURE__ */
|
|
3180
|
+
/* @__PURE__ */ jsx11("h1", { style: styles.resultTitle, children: "Document expired" }),
|
|
3181
|
+
/* @__PURE__ */ jsx11("p", { style: styles.resultSubtitle, children: "The document you submitted has expired. Please use a valid, non-expired document." }),
|
|
3182
|
+
verification.documentVerification && /* @__PURE__ */ jsxs10("div", { style: styles.expiryCard, children: [
|
|
3183
|
+
/* @__PURE__ */ jsxs10("div", { style: styles.expiryRow, children: [
|
|
3184
|
+
/* @__PURE__ */ jsx11("span", { style: styles.expiryLabel, children: "Document type" }),
|
|
3185
|
+
/* @__PURE__ */ jsx11("span", { style: styles.expiryValue, children: verification.documentVerification.documentType || "ID Card" })
|
|
2743
3186
|
] }),
|
|
2744
|
-
verification.documentVerification.issuingCountry && /* @__PURE__ */
|
|
2745
|
-
/* @__PURE__ */
|
|
2746
|
-
/* @__PURE__ */
|
|
3187
|
+
verification.documentVerification.issuingCountry && /* @__PURE__ */ jsxs10("div", { style: styles.expiryRow, children: [
|
|
3188
|
+
/* @__PURE__ */ jsx11("span", { style: styles.expiryLabel, children: "Country" }),
|
|
3189
|
+
/* @__PURE__ */ jsx11("span", { style: styles.expiryValue, children: verification.documentVerification.issuingCountry })
|
|
2747
3190
|
] }),
|
|
2748
|
-
verification.documentVerification.expirationDate && /* @__PURE__ */
|
|
2749
|
-
/* @__PURE__ */
|
|
2750
|
-
/* @__PURE__ */
|
|
3191
|
+
verification.documentVerification.expirationDate && /* @__PURE__ */ jsxs10("div", { style: styles.expiryRow, children: [
|
|
3192
|
+
/* @__PURE__ */ jsx11("span", { style: styles.expiryLabel, children: "Expired on" }),
|
|
3193
|
+
/* @__PURE__ */ jsx11("span", { style: styles.expiryBadge, children: verification.documentVerification.expirationDate })
|
|
2751
3194
|
] })
|
|
2752
3195
|
] }),
|
|
2753
|
-
/* @__PURE__ */
|
|
2754
|
-
/* @__PURE__ */
|
|
2755
|
-
/* @__PURE__ */
|
|
2756
|
-
/* @__PURE__ */
|
|
3196
|
+
/* @__PURE__ */ jsxs10("div", { style: { textAlign: "left" }, children: [
|
|
3197
|
+
/* @__PURE__ */ jsx11(GuidanceTip, { number: 1, text: "Check the expiration date on your document" }),
|
|
3198
|
+
/* @__PURE__ */ jsx11(GuidanceTip, { number: 2, text: "Use a different document that is currently valid" }),
|
|
3199
|
+
/* @__PURE__ */ jsx11(GuidanceTip, { number: 3, text: "Ensure the document details are clearly visible" })
|
|
2757
3200
|
] })
|
|
2758
3201
|
] }),
|
|
2759
|
-
/* @__PURE__ */
|
|
3202
|
+
/* @__PURE__ */ jsx11("div", { style: styles.footer, children: /* @__PURE__ */ jsx11("button", { style: styles.primaryButton, onClick: onRetry, children: "Try with a valid document" }) })
|
|
2760
3203
|
] });
|
|
2761
3204
|
}
|
|
2762
3205
|
function ManualReviewResult({ verification, onDone }) {
|
|
@@ -2764,9 +3207,9 @@ function ManualReviewResult({ verification, onDone }) {
|
|
|
2764
3207
|
verification.scores?.overall ?? 100 - (verification.riskScore ?? 32)
|
|
2765
3208
|
);
|
|
2766
3209
|
const metrics = computeScoreBreakdown(verification);
|
|
2767
|
-
return /* @__PURE__ */
|
|
2768
|
-
/* @__PURE__ */
|
|
2769
|
-
/* @__PURE__ */
|
|
3210
|
+
return /* @__PURE__ */ jsxs10("div", { style: styles.resultContainer, children: [
|
|
3211
|
+
/* @__PURE__ */ jsxs10("div", { style: styles.resultContent, children: [
|
|
3212
|
+
/* @__PURE__ */ jsx11(
|
|
2770
3213
|
"div",
|
|
2771
3214
|
{
|
|
2772
3215
|
style: {
|
|
@@ -2774,7 +3217,7 @@ function ManualReviewResult({ verification, onDone }) {
|
|
|
2774
3217
|
backgroundColor: `${colors.info}15`,
|
|
2775
3218
|
border: `2px solid ${colors.info}30`
|
|
2776
3219
|
},
|
|
2777
|
-
children: /* @__PURE__ */
|
|
3220
|
+
children: /* @__PURE__ */ jsx11(
|
|
2778
3221
|
"div",
|
|
2779
3222
|
{
|
|
2780
3223
|
style: {
|
|
@@ -2788,9 +3231,9 @@ function ManualReviewResult({ verification, onDone }) {
|
|
|
2788
3231
|
)
|
|
2789
3232
|
}
|
|
2790
3233
|
),
|
|
2791
|
-
/* @__PURE__ */
|
|
2792
|
-
/* @__PURE__ */
|
|
2793
|
-
/* @__PURE__ */
|
|
3234
|
+
/* @__PURE__ */ jsx11("h1", { style: styles.resultTitle, children: "Under review" }),
|
|
3235
|
+
/* @__PURE__ */ jsx11("p", { style: styles.resultSubtitle, children: "Your verification requires manual review. We'll notify you of the result." }),
|
|
3236
|
+
/* @__PURE__ */ jsx11(
|
|
2794
3237
|
ScoreCard,
|
|
2795
3238
|
{
|
|
2796
3239
|
score,
|
|
@@ -2798,21 +3241,21 @@ function ManualReviewResult({ verification, onDone }) {
|
|
|
2798
3241
|
gradient: `linear-gradient(135deg, ${colors.info}, #0369A1)`
|
|
2799
3242
|
}
|
|
2800
3243
|
),
|
|
2801
|
-
metrics.map((m, i) => /* @__PURE__ */
|
|
3244
|
+
metrics.map((m, i) => /* @__PURE__ */ jsx11(ScoreMetricRow, { ...m }, i))
|
|
2802
3245
|
] }),
|
|
2803
|
-
/* @__PURE__ */
|
|
3246
|
+
/* @__PURE__ */ jsx11("div", { style: styles.footer, children: /* @__PURE__ */ jsx11("button", { style: styles.primaryButton, onClick: onDone, children: "Got it" }) })
|
|
2804
3247
|
] });
|
|
2805
3248
|
}
|
|
2806
3249
|
function GuidanceTip({ number, text }) {
|
|
2807
|
-
return /* @__PURE__ */
|
|
2808
|
-
/* @__PURE__ */
|
|
2809
|
-
/* @__PURE__ */
|
|
3250
|
+
return /* @__PURE__ */ jsxs10("div", { style: styles.guidanceTip, children: [
|
|
3251
|
+
/* @__PURE__ */ jsx11("div", { style: styles.guidanceTipNumber, children: number }),
|
|
3252
|
+
/* @__PURE__ */ jsx11("span", { style: styles.guidanceTipText, children: text })
|
|
2810
3253
|
] });
|
|
2811
3254
|
}
|
|
2812
3255
|
function SimplifiedSuccess({ onDone, customMessages }) {
|
|
2813
|
-
return /* @__PURE__ */
|
|
2814
|
-
/* @__PURE__ */
|
|
2815
|
-
/* @__PURE__ */
|
|
3256
|
+
return /* @__PURE__ */ jsxs10("div", { style: styles.resultContainer, children: [
|
|
3257
|
+
/* @__PURE__ */ jsxs10("div", { style: { ...styles.resultContent, textAlign: "center" }, children: [
|
|
3258
|
+
/* @__PURE__ */ jsx11(
|
|
2816
3259
|
"div",
|
|
2817
3260
|
{
|
|
2818
3261
|
style: {
|
|
@@ -2821,7 +3264,7 @@ function SimplifiedSuccess({ onDone, customMessages }) {
|
|
|
2821
3264
|
width: 96,
|
|
2822
3265
|
height: 96
|
|
2823
3266
|
},
|
|
2824
|
-
children: /* @__PURE__ */
|
|
3267
|
+
children: /* @__PURE__ */ jsx11(
|
|
2825
3268
|
"div",
|
|
2826
3269
|
{
|
|
2827
3270
|
style: {
|
|
@@ -2838,16 +3281,16 @@ function SimplifiedSuccess({ onDone, customMessages }) {
|
|
|
2838
3281
|
)
|
|
2839
3282
|
}
|
|
2840
3283
|
),
|
|
2841
|
-
/* @__PURE__ */
|
|
2842
|
-
/* @__PURE__ */
|
|
3284
|
+
/* @__PURE__ */ jsx11("h1", { style: { ...styles.resultTitle, fontSize: 24, marginTop: 16 }, children: customMessages?.successTitle || "Verification Successful" }),
|
|
3285
|
+
/* @__PURE__ */ jsx11("p", { style: { ...styles.resultSubtitle, fontSize: 16, maxWidth: 320, margin: "8px auto 0" }, children: customMessages?.successMessage || "Your identity has been successfully verified. You can now proceed." })
|
|
2843
3286
|
] }),
|
|
2844
|
-
/* @__PURE__ */
|
|
3287
|
+
/* @__PURE__ */ jsx11("div", { style: styles.footer, children: /* @__PURE__ */ jsx11("button", { style: styles.primaryButton, onClick: onDone, children: "Continue" }) })
|
|
2845
3288
|
] });
|
|
2846
3289
|
}
|
|
2847
3290
|
function SimplifiedFailed({ onRetry, customMessages }) {
|
|
2848
|
-
return /* @__PURE__ */
|
|
2849
|
-
/* @__PURE__ */
|
|
2850
|
-
/* @__PURE__ */
|
|
3291
|
+
return /* @__PURE__ */ jsxs10("div", { style: styles.resultContainer, children: [
|
|
3292
|
+
/* @__PURE__ */ jsxs10("div", { style: { ...styles.resultContent, textAlign: "center" }, children: [
|
|
3293
|
+
/* @__PURE__ */ jsx11(
|
|
2851
3294
|
"div",
|
|
2852
3295
|
{
|
|
2853
3296
|
style: {
|
|
@@ -2856,7 +3299,7 @@ function SimplifiedFailed({ onRetry, customMessages }) {
|
|
|
2856
3299
|
width: 96,
|
|
2857
3300
|
height: 96
|
|
2858
3301
|
},
|
|
2859
|
-
children: /* @__PURE__ */
|
|
3302
|
+
children: /* @__PURE__ */ jsx11(
|
|
2860
3303
|
"div",
|
|
2861
3304
|
{
|
|
2862
3305
|
style: {
|
|
@@ -2873,16 +3316,16 @@ function SimplifiedFailed({ onRetry, customMessages }) {
|
|
|
2873
3316
|
)
|
|
2874
3317
|
}
|
|
2875
3318
|
),
|
|
2876
|
-
/* @__PURE__ */
|
|
2877
|
-
/* @__PURE__ */
|
|
3319
|
+
/* @__PURE__ */ jsx11("h1", { style: { ...styles.resultTitle, fontSize: 24, marginTop: 16 }, children: customMessages?.failedTitle || "Verification Failed" }),
|
|
3320
|
+
/* @__PURE__ */ jsx11("p", { style: { ...styles.resultSubtitle, fontSize: 16, maxWidth: 320, margin: "8px auto 0" }, children: customMessages?.failedMessage || "We could not verify your identity. Please try again with a valid document." })
|
|
2878
3321
|
] }),
|
|
2879
|
-
/* @__PURE__ */
|
|
3322
|
+
/* @__PURE__ */ jsx11("div", { style: styles.footer, children: /* @__PURE__ */ jsx11("button", { style: styles.primaryButton, onClick: onRetry, children: "Try Again" }) })
|
|
2880
3323
|
] });
|
|
2881
3324
|
}
|
|
2882
3325
|
function SimplifiedReview({ verification, onDone, customMessages }) {
|
|
2883
|
-
return /* @__PURE__ */
|
|
2884
|
-
/* @__PURE__ */
|
|
2885
|
-
/* @__PURE__ */
|
|
3326
|
+
return /* @__PURE__ */ jsxs10("div", { style: styles.resultContainer, children: [
|
|
3327
|
+
/* @__PURE__ */ jsxs10("div", { style: { ...styles.resultContent, textAlign: "center" }, children: [
|
|
3328
|
+
/* @__PURE__ */ jsx11(
|
|
2886
3329
|
"div",
|
|
2887
3330
|
{
|
|
2888
3331
|
style: {
|
|
@@ -2891,7 +3334,7 @@ function SimplifiedReview({ verification, onDone, customMessages }) {
|
|
|
2891
3334
|
width: 96,
|
|
2892
3335
|
height: 96
|
|
2893
3336
|
},
|
|
2894
|
-
children: /* @__PURE__ */
|
|
3337
|
+
children: /* @__PURE__ */ jsx11(
|
|
2895
3338
|
"div",
|
|
2896
3339
|
{
|
|
2897
3340
|
style: {
|
|
@@ -2908,9 +3351,9 @@ function SimplifiedReview({ verification, onDone, customMessages }) {
|
|
|
2908
3351
|
)
|
|
2909
3352
|
}
|
|
2910
3353
|
),
|
|
2911
|
-
/* @__PURE__ */
|
|
2912
|
-
/* @__PURE__ */
|
|
2913
|
-
/* @__PURE__ */
|
|
3354
|
+
/* @__PURE__ */ jsx11("h1", { style: { ...styles.resultTitle, fontSize: 24, marginTop: 16 }, children: customMessages?.reviewTitle || "Verification Under Review" }),
|
|
3355
|
+
/* @__PURE__ */ jsx11("p", { style: { ...styles.resultSubtitle, fontSize: 16, maxWidth: 320, margin: "8px auto 0" }, children: customMessages?.reviewMessage || "Your verification requires additional review. We will notify you of the result." }),
|
|
3356
|
+
/* @__PURE__ */ jsxs10("div", { style: {
|
|
2914
3357
|
marginTop: 24,
|
|
2915
3358
|
padding: "12px 24px",
|
|
2916
3359
|
backgroundColor: `${colors.info}10`,
|
|
@@ -2918,27 +3361,27 @@ function SimplifiedReview({ verification, onDone, customMessages }) {
|
|
|
2918
3361
|
border: `1px solid ${colors.info}30`,
|
|
2919
3362
|
display: "inline-block"
|
|
2920
3363
|
}, children: [
|
|
2921
|
-
/* @__PURE__ */
|
|
2922
|
-
/* @__PURE__ */
|
|
3364
|
+
/* @__PURE__ */ jsx11("span", { style: { fontSize: 12, color: colors.textSecondary }, children: "Reference: " }),
|
|
3365
|
+
/* @__PURE__ */ jsx11("span", { style: { fontSize: 14, fontWeight: 600, fontFamily: "monospace" }, children: verification.id.slice(0, 8) })
|
|
2923
3366
|
] })
|
|
2924
3367
|
] }),
|
|
2925
|
-
/* @__PURE__ */
|
|
3368
|
+
/* @__PURE__ */ jsx11("div", { style: styles.footer, children: /* @__PURE__ */ jsx11("button", { style: styles.primaryButton, onClick: onDone, children: "Got It" }) })
|
|
2926
3369
|
] });
|
|
2927
3370
|
}
|
|
2928
3371
|
|
|
2929
3372
|
// src/components/ErrorScreen.tsx
|
|
2930
|
-
import { jsx as
|
|
3373
|
+
import { jsx as jsx12, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
2931
3374
|
function ErrorScreen({ error, onRetry, onCancel }) {
|
|
2932
|
-
return /* @__PURE__ */
|
|
2933
|
-
/* @__PURE__ */
|
|
2934
|
-
/* @__PURE__ */
|
|
3375
|
+
return /* @__PURE__ */ jsxs11("div", { style: styles.resultContainer, children: [
|
|
3376
|
+
/* @__PURE__ */ jsxs11("div", { style: styles.resultContent, children: [
|
|
3377
|
+
/* @__PURE__ */ jsx12(
|
|
2935
3378
|
"div",
|
|
2936
3379
|
{
|
|
2937
3380
|
style: {
|
|
2938
3381
|
...styles.resultIconOuterRing,
|
|
2939
3382
|
backgroundColor: colors.errorBg
|
|
2940
3383
|
},
|
|
2941
|
-
children: /* @__PURE__ */
|
|
3384
|
+
children: /* @__PURE__ */ jsx12(
|
|
2942
3385
|
"div",
|
|
2943
3386
|
{
|
|
2944
3387
|
style: {
|
|
@@ -2952,26 +3395,26 @@ function ErrorScreen({ error, onRetry, onCancel }) {
|
|
|
2952
3395
|
)
|
|
2953
3396
|
}
|
|
2954
3397
|
),
|
|
2955
|
-
/* @__PURE__ */
|
|
2956
|
-
/* @__PURE__ */
|
|
2957
|
-
error.recoverySuggestion && /* @__PURE__ */
|
|
3398
|
+
/* @__PURE__ */ jsx12("h1", { style: styles.resultTitle, children: "Something went wrong" }),
|
|
3399
|
+
/* @__PURE__ */ jsx12("p", { style: styles.resultSubtitle, children: error.message }),
|
|
3400
|
+
error.recoverySuggestion && /* @__PURE__ */ jsx12("p", { style: { ...styles.bodyText, marginTop: "12px" }, children: error.recoverySuggestion })
|
|
2958
3401
|
] }),
|
|
2959
|
-
/* @__PURE__ */
|
|
2960
|
-
error.isRetryable && /* @__PURE__ */
|
|
2961
|
-
/* @__PURE__ */
|
|
3402
|
+
/* @__PURE__ */ jsxs11("div", { style: styles.footer, children: [
|
|
3403
|
+
error.isRetryable && /* @__PURE__ */ jsx12("button", { style: styles.primaryButton, onClick: onRetry, children: "Try Again" }),
|
|
3404
|
+
/* @__PURE__ */ jsx12("button", { style: styles.textButton, onClick: onCancel, children: "Cancel" })
|
|
2962
3405
|
] })
|
|
2963
3406
|
] });
|
|
2964
3407
|
}
|
|
2965
3408
|
|
|
2966
3409
|
// src/components/LoadingScreen.tsx
|
|
2967
|
-
import { useEffect as
|
|
2968
|
-
import { jsx as
|
|
3410
|
+
import { useEffect as useEffect10 } from "react";
|
|
3411
|
+
import { jsx as jsx13, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
2969
3412
|
function LoadingScreen({ message = "Loading..." }) {
|
|
2970
|
-
|
|
3413
|
+
useEffect10(() => {
|
|
2971
3414
|
injectKeyframes();
|
|
2972
3415
|
}, []);
|
|
2973
|
-
return /* @__PURE__ */
|
|
2974
|
-
/* @__PURE__ */
|
|
3416
|
+
return /* @__PURE__ */ jsx13("div", { style: styles.container, children: /* @__PURE__ */ jsxs12("div", { style: styles.loadingContainer, children: [
|
|
3417
|
+
/* @__PURE__ */ jsx13(
|
|
2975
3418
|
"div",
|
|
2976
3419
|
{
|
|
2977
3420
|
style: {
|
|
@@ -2988,18 +3431,19 @@ function LoadingScreen({ message = "Loading..." }) {
|
|
|
2988
3431
|
children: "\u{1F6E1}\uFE0F"
|
|
2989
3432
|
}
|
|
2990
3433
|
),
|
|
2991
|
-
/* @__PURE__ */
|
|
3434
|
+
/* @__PURE__ */ jsx13("p", { style: styles.loadingText, children: message })
|
|
2992
3435
|
] }) });
|
|
2993
3436
|
}
|
|
2994
3437
|
|
|
2995
3438
|
// src/components/VerificationFlow.tsx
|
|
2996
|
-
import { jsx as
|
|
3439
|
+
import { jsx as jsx14, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
2997
3440
|
function VerificationFlow({
|
|
2998
3441
|
externalId,
|
|
2999
3442
|
tier = "standard",
|
|
3000
3443
|
documentTypes,
|
|
3001
3444
|
expectedFirstName,
|
|
3002
3445
|
expectedLastName,
|
|
3446
|
+
showVisualGuides = true,
|
|
3003
3447
|
onComplete,
|
|
3004
3448
|
onError,
|
|
3005
3449
|
onCancel,
|
|
@@ -3021,20 +3465,20 @@ function VerificationFlow({
|
|
|
3021
3465
|
retry,
|
|
3022
3466
|
sdk
|
|
3023
3467
|
} = useKoraIDV();
|
|
3024
|
-
const [selectedCountry, setSelectedCountry] =
|
|
3025
|
-
const [flowStep, setFlowStep] =
|
|
3026
|
-
const [showFlipInstruction, setShowFlipInstruction] =
|
|
3027
|
-
const [supportedCountries, setSupportedCountries] =
|
|
3028
|
-
const [countriesLoading, setCountriesLoading] =
|
|
3029
|
-
|
|
3468
|
+
const [selectedCountry, setSelectedCountry] = useState9(null);
|
|
3469
|
+
const [flowStep, setFlowStep] = useState9("consent");
|
|
3470
|
+
const [showFlipInstruction, setShowFlipInstruction] = useState9(true);
|
|
3471
|
+
const [supportedCountries, setSupportedCountries] = useState9([]);
|
|
3472
|
+
const [countriesLoading, setCountriesLoading] = useState9(false);
|
|
3473
|
+
useEffect11(() => {
|
|
3030
3474
|
if (state.step === "document_front") {
|
|
3031
3475
|
setShowFlipInstruction(true);
|
|
3032
3476
|
}
|
|
3033
3477
|
}, [state.step]);
|
|
3034
|
-
|
|
3478
|
+
useEffect11(() => {
|
|
3035
3479
|
startVerification(externalId, tier, expectedFirstName, expectedLastName);
|
|
3036
3480
|
}, [externalId, tier, expectedFirstName, expectedLastName, startVerification]);
|
|
3037
|
-
|
|
3481
|
+
useEffect11(() => {
|
|
3038
3482
|
if (state.error && onError) {
|
|
3039
3483
|
onError(state.error);
|
|
3040
3484
|
}
|
|
@@ -3080,19 +3524,19 @@ function VerificationFlow({
|
|
|
3080
3524
|
...style
|
|
3081
3525
|
};
|
|
3082
3526
|
if (state.error) {
|
|
3083
|
-
return /* @__PURE__ */
|
|
3527
|
+
return /* @__PURE__ */ jsx14("div", { className, style: containerStyle, children: /* @__PURE__ */ jsx14(ErrorScreen, { error: state.error, onRetry: retry, onCancel: handleCancel }) });
|
|
3084
3528
|
}
|
|
3085
3529
|
if (state.isLoading && state.step !== "processing") {
|
|
3086
|
-
return /* @__PURE__ */
|
|
3530
|
+
return /* @__PURE__ */ jsx14("div", { className, style: containerStyle, children: /* @__PURE__ */ jsx14(LoadingScreen, {}) });
|
|
3087
3531
|
}
|
|
3088
3532
|
if (flowStep === "consent" && state.step === "consent") {
|
|
3089
|
-
return /* @__PURE__ */
|
|
3533
|
+
return /* @__PURE__ */ jsx14("div", { className, style: containerStyle, children: /* @__PURE__ */ jsx14(ConsentScreen, { onAccept: handleAcceptConsent, onDecline: handleCancel }) });
|
|
3090
3534
|
}
|
|
3091
3535
|
if (flowStep === "country_selection") {
|
|
3092
3536
|
if (countriesLoading) {
|
|
3093
|
-
return /* @__PURE__ */
|
|
3537
|
+
return /* @__PURE__ */ jsx14("div", { className, style: containerStyle, children: /* @__PURE__ */ jsx14(LoadingScreen, {}) });
|
|
3094
3538
|
}
|
|
3095
|
-
return /* @__PURE__ */
|
|
3539
|
+
return /* @__PURE__ */ jsx14("div", { className, style: containerStyle, children: /* @__PURE__ */ jsx14(
|
|
3096
3540
|
CountrySelectionScreen,
|
|
3097
3541
|
{
|
|
3098
3542
|
countries: supportedCountries,
|
|
@@ -3101,8 +3545,8 @@ function VerificationFlow({
|
|
|
3101
3545
|
}
|
|
3102
3546
|
) });
|
|
3103
3547
|
}
|
|
3104
|
-
return /* @__PURE__ */
|
|
3105
|
-
state.step === "document_selection" && /* @__PURE__ */
|
|
3548
|
+
return /* @__PURE__ */ jsxs13("div", { className, style: containerStyle, children: [
|
|
3549
|
+
state.step === "document_selection" && /* @__PURE__ */ jsx14(
|
|
3106
3550
|
DocumentSelectionScreen,
|
|
3107
3551
|
{
|
|
3108
3552
|
documentTypes,
|
|
@@ -3111,32 +3555,41 @@ function VerificationFlow({
|
|
|
3111
3555
|
onCancel: handleCancel
|
|
3112
3556
|
}
|
|
3113
3557
|
),
|
|
3114
|
-
state.step === "document_front" && /* @__PURE__ */
|
|
3558
|
+
state.step === "document_front" && /* @__PURE__ */ jsx14(
|
|
3115
3559
|
DocumentCaptureScreen,
|
|
3116
3560
|
{
|
|
3117
3561
|
side: "front",
|
|
3118
3562
|
onQualityCheck: (blob) => checkDocumentQuality(blob),
|
|
3119
3563
|
onCapture: (imageData) => uploadDocument(imageData, "front", selectedCountry?.id),
|
|
3120
|
-
onCancel: handleCancel
|
|
3564
|
+
onCancel: handleCancel,
|
|
3565
|
+
showVisualGuides
|
|
3121
3566
|
}
|
|
3122
3567
|
),
|
|
3123
|
-
state.step === "document_back" && showFlipInstruction && /* @__PURE__ */
|
|
3568
|
+
state.step === "document_back" && showFlipInstruction && /* @__PURE__ */ jsx14(
|
|
3124
3569
|
FlipDocumentScreen,
|
|
3125
3570
|
{
|
|
3126
3571
|
onContinue: () => setShowFlipInstruction(false),
|
|
3127
3572
|
onCancel: handleCancel
|
|
3128
3573
|
}
|
|
3129
3574
|
),
|
|
3130
|
-
state.step === "document_back" && !showFlipInstruction && /* @__PURE__ */
|
|
3575
|
+
state.step === "document_back" && !showFlipInstruction && /* @__PURE__ */ jsx14(
|
|
3131
3576
|
DocumentCaptureScreen,
|
|
3132
3577
|
{
|
|
3133
3578
|
side: "back",
|
|
3134
3579
|
onCapture: (imageData) => uploadDocument(imageData, "back", selectedCountry?.id),
|
|
3135
|
-
onCancel: handleCancel
|
|
3580
|
+
onCancel: handleCancel,
|
|
3581
|
+
showVisualGuides
|
|
3136
3582
|
}
|
|
3137
3583
|
),
|
|
3138
|
-
state.step === "selfie" && /* @__PURE__ */
|
|
3139
|
-
|
|
3584
|
+
state.step === "selfie" && /* @__PURE__ */ jsx14(
|
|
3585
|
+
SelfieCaptureScreen,
|
|
3586
|
+
{
|
|
3587
|
+
onCapture: uploadSelfie,
|
|
3588
|
+
onCancel: handleCancel,
|
|
3589
|
+
showVisualGuides
|
|
3590
|
+
}
|
|
3591
|
+
),
|
|
3592
|
+
state.step === "liveness" && /* @__PURE__ */ jsx14(
|
|
3140
3593
|
LivenessScreen,
|
|
3141
3594
|
{
|
|
3142
3595
|
session: state.livenessSession,
|
|
@@ -3145,20 +3598,13 @@ function VerificationFlow({
|
|
|
3145
3598
|
onChallengeComplete: submitChallenge,
|
|
3146
3599
|
onStart: startLiveness,
|
|
3147
3600
|
onComplete: complete,
|
|
3148
|
-
onCancel: handleCancel
|
|
3601
|
+
onCancel: handleCancel,
|
|
3602
|
+
lastChallengeError: state.lastChallengeError,
|
|
3603
|
+
showVisualGuides
|
|
3149
3604
|
}
|
|
3150
3605
|
),
|
|
3151
|
-
state.step === "processing" && /* @__PURE__ */
|
|
3152
|
-
|
|
3153
|
-
{
|
|
3154
|
-
steps: [
|
|
3155
|
-
{ label: "Document analyzed", status: "done" },
|
|
3156
|
-
{ label: "Checking face match", status: "active" },
|
|
3157
|
-
{ label: "Finalizing results", status: "pending" }
|
|
3158
|
-
]
|
|
3159
|
-
}
|
|
3160
|
-
),
|
|
3161
|
-
state.step === "complete" && state.verification && /* @__PURE__ */ jsx13(
|
|
3606
|
+
state.step === "processing" && /* @__PURE__ */ jsx14(ProcessingScreen, {}),
|
|
3607
|
+
state.step === "complete" && state.verification && /* @__PURE__ */ jsx14(
|
|
3162
3608
|
ResultScreen,
|
|
3163
3609
|
{
|
|
3164
3610
|
verification: state.verification,
|
|
@@ -3170,8 +3616,8 @@ function VerificationFlow({
|
|
|
3170
3616
|
}
|
|
3171
3617
|
|
|
3172
3618
|
// src/components/QrHandoffScreen.tsx
|
|
3173
|
-
import { useEffect as
|
|
3174
|
-
import { jsx as
|
|
3619
|
+
import { useEffect as useEffect12, useState as useState10, useRef as useRef7 } from "react";
|
|
3620
|
+
import { jsx as jsx15, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
3175
3621
|
function QrHandoffScreen({
|
|
3176
3622
|
session,
|
|
3177
3623
|
onMobileCaptureComplete,
|
|
@@ -3180,11 +3626,11 @@ function QrHandoffScreen({
|
|
|
3180
3626
|
onRefresh,
|
|
3181
3627
|
eventSource
|
|
3182
3628
|
}) {
|
|
3183
|
-
const [timeLeft, setTimeLeft] =
|
|
3184
|
-
const [scanned, setScanned] =
|
|
3185
|
-
const [expired, setExpired] =
|
|
3186
|
-
const timerRef =
|
|
3187
|
-
|
|
3629
|
+
const [timeLeft, setTimeLeft] = useState10(session.expiresIn);
|
|
3630
|
+
const [scanned, setScanned] = useState10(false);
|
|
3631
|
+
const [expired, setExpired] = useState10(false);
|
|
3632
|
+
const timerRef = useRef7();
|
|
3633
|
+
useEffect12(() => {
|
|
3188
3634
|
setTimeLeft(session.expiresIn);
|
|
3189
3635
|
setExpired(false);
|
|
3190
3636
|
setScanned(false);
|
|
@@ -3201,7 +3647,7 @@ function QrHandoffScreen({
|
|
|
3201
3647
|
}, 1e3);
|
|
3202
3648
|
return () => clearInterval(timerRef.current);
|
|
3203
3649
|
}, [session.token]);
|
|
3204
|
-
|
|
3650
|
+
useEffect12(() => {
|
|
3205
3651
|
if (!eventSource) return;
|
|
3206
3652
|
const handleStatus = (event) => {
|
|
3207
3653
|
try {
|
|
@@ -3227,34 +3673,34 @@ function QrHandoffScreen({
|
|
|
3227
3673
|
const seconds = timeLeft % 60;
|
|
3228
3674
|
const qrSize = 200;
|
|
3229
3675
|
if (expired) {
|
|
3230
|
-
return /* @__PURE__ */
|
|
3231
|
-
/* @__PURE__ */
|
|
3232
|
-
/* @__PURE__ */
|
|
3233
|
-
/* @__PURE__ */
|
|
3234
|
-
/* @__PURE__ */
|
|
3235
|
-
/* @__PURE__ */
|
|
3676
|
+
return /* @__PURE__ */ jsx15("div", { style: qrStyles.container, children: /* @__PURE__ */ jsxs14("div", { style: qrStyles.content, children: [
|
|
3677
|
+
/* @__PURE__ */ jsx15("div", { style: qrStyles.expiredIcon, children: "\u23F1" }),
|
|
3678
|
+
/* @__PURE__ */ jsx15("h2", { style: qrStyles.title, children: "QR Code Expired" }),
|
|
3679
|
+
/* @__PURE__ */ jsx15("p", { style: qrStyles.subtitle, children: "The QR code has expired. Generate a new one to continue." }),
|
|
3680
|
+
/* @__PURE__ */ jsx15("button", { style: qrStyles.primaryButton, onClick: onRefresh, children: "Generate New QR Code" }),
|
|
3681
|
+
/* @__PURE__ */ jsx15("button", { style: qrStyles.secondaryButton, onClick: onContinueOnDevice, children: "Continue on this device instead" })
|
|
3236
3682
|
] }) });
|
|
3237
3683
|
}
|
|
3238
3684
|
if (scanned) {
|
|
3239
|
-
return /* @__PURE__ */
|
|
3240
|
-
/* @__PURE__ */
|
|
3241
|
-
/* @__PURE__ */
|
|
3242
|
-
/* @__PURE__ */
|
|
3243
|
-
/* @__PURE__ */
|
|
3244
|
-
/* @__PURE__ */
|
|
3685
|
+
return /* @__PURE__ */ jsx15("div", { style: qrStyles.container, children: /* @__PURE__ */ jsxs14("div", { style: qrStyles.content, children: [
|
|
3686
|
+
/* @__PURE__ */ jsx15("div", { style: qrStyles.spinnerContainer, children: /* @__PURE__ */ jsx15("div", { style: qrStyles.spinner }) }),
|
|
3687
|
+
/* @__PURE__ */ jsx15("h2", { style: qrStyles.title, children: "Capturing on your phone..." }),
|
|
3688
|
+
/* @__PURE__ */ jsx15("p", { style: qrStyles.subtitle, children: "Complete the document scan and selfie on your mobile device. This page will update automatically when done." }),
|
|
3689
|
+
/* @__PURE__ */ jsxs14("div", { style: qrStyles.statusBadge, children: [
|
|
3690
|
+
/* @__PURE__ */ jsx15("span", { style: qrStyles.statusDot }),
|
|
3245
3691
|
"Connected \u2014 waiting for capture"
|
|
3246
3692
|
] })
|
|
3247
3693
|
] }) });
|
|
3248
3694
|
}
|
|
3249
|
-
return /* @__PURE__ */
|
|
3250
|
-
/* @__PURE__ */
|
|
3251
|
-
/* @__PURE__ */
|
|
3252
|
-
/* @__PURE__ */
|
|
3253
|
-
/* @__PURE__ */
|
|
3695
|
+
return /* @__PURE__ */ jsx15("div", { style: qrStyles.container, children: /* @__PURE__ */ jsxs14("div", { style: qrStyles.content, children: [
|
|
3696
|
+
/* @__PURE__ */ jsx15("h2", { style: qrStyles.title, children: "Scan with your phone" }),
|
|
3697
|
+
/* @__PURE__ */ jsx15("p", { style: qrStyles.subtitle, children: "Use your phone's camera for a better capture experience. Scan the QR code below to continue on your mobile device." }),
|
|
3698
|
+
/* @__PURE__ */ jsxs14("div", { style: qrStyles.qrContainer, children: [
|
|
3699
|
+
/* @__PURE__ */ jsx15("div", { style: {
|
|
3254
3700
|
...qrStyles.qrBox,
|
|
3255
3701
|
width: qrSize,
|
|
3256
3702
|
height: qrSize
|
|
3257
|
-
}, children: /* @__PURE__ */
|
|
3703
|
+
}, children: /* @__PURE__ */ jsx15(
|
|
3258
3704
|
"img",
|
|
3259
3705
|
{
|
|
3260
3706
|
src: `https://api.qrserver.com/v1/create-qr-code/?size=${qrSize}x${qrSize}&data=${encodeURIComponent(session.captureUrl)}&margin=8`,
|
|
@@ -3264,26 +3710,26 @@ function QrHandoffScreen({
|
|
|
3264
3710
|
style: { borderRadius: 12 }
|
|
3265
3711
|
}
|
|
3266
3712
|
) }),
|
|
3267
|
-
/* @__PURE__ */
|
|
3713
|
+
/* @__PURE__ */ jsxs14("div", { style: qrStyles.timer, children: [
|
|
3268
3714
|
minutes,
|
|
3269
3715
|
":",
|
|
3270
3716
|
seconds.toString().padStart(2, "0"),
|
|
3271
3717
|
" remaining"
|
|
3272
3718
|
] })
|
|
3273
3719
|
] }),
|
|
3274
|
-
/* @__PURE__ */
|
|
3275
|
-
/* @__PURE__ */
|
|
3276
|
-
/* @__PURE__ */
|
|
3277
|
-
/* @__PURE__ */
|
|
3720
|
+
/* @__PURE__ */ jsxs14("div", { style: qrStyles.steps, children: [
|
|
3721
|
+
/* @__PURE__ */ jsx15(Step, { number: 1, text: "Open your phone's camera" }),
|
|
3722
|
+
/* @__PURE__ */ jsx15(Step, { number: 2, text: "Point at the QR code" }),
|
|
3723
|
+
/* @__PURE__ */ jsx15(Step, { number: 3, text: "Complete the capture on your phone" })
|
|
3278
3724
|
] }),
|
|
3279
|
-
/* @__PURE__ */
|
|
3280
|
-
/* @__PURE__ */
|
|
3725
|
+
/* @__PURE__ */ jsx15("div", { style: qrStyles.divider, children: /* @__PURE__ */ jsx15("span", { style: qrStyles.dividerText, children: "or" }) }),
|
|
3726
|
+
/* @__PURE__ */ jsx15("button", { style: qrStyles.secondaryButton, onClick: onContinueOnDevice, children: "Continue on this device" })
|
|
3281
3727
|
] }) });
|
|
3282
3728
|
}
|
|
3283
3729
|
function Step({ number, text }) {
|
|
3284
|
-
return /* @__PURE__ */
|
|
3285
|
-
/* @__PURE__ */
|
|
3286
|
-
/* @__PURE__ */
|
|
3730
|
+
return /* @__PURE__ */ jsxs14("div", { style: qrStyles.step, children: [
|
|
3731
|
+
/* @__PURE__ */ jsx15("div", { style: qrStyles.stepNumber, children: number }),
|
|
3732
|
+
/* @__PURE__ */ jsx15("span", { style: qrStyles.stepText, children: text })
|
|
3287
3733
|
] });
|
|
3288
3734
|
}
|
|
3289
3735
|
var qrStyles = {
|