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