@neofaceid/web-sdk 1.3.0 → 1.4.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.
|
@@ -1239,6 +1239,41 @@ const getOnboardingDetails = async (applicationToken, onboardingToken) => {
|
|
|
1239
1239
|
throw new NeoFaceError("Erro desconhecido", ErrorType.NETWORK_ERROR);
|
|
1240
1240
|
}
|
|
1241
1241
|
};
|
|
1242
|
+
const validateOnboardingToken = async (applicationToken, onboardingToken) => {
|
|
1243
|
+
ensureSecureContext();
|
|
1244
|
+
const controller = createTimeoutController();
|
|
1245
|
+
try {
|
|
1246
|
+
const response = await fetch(`${API_BASE_URL}/api/v1/onboardings/${encodeURIComponent(onboardingToken)}/`, {
|
|
1247
|
+
method: "GET",
|
|
1248
|
+
headers: {
|
|
1249
|
+
"X-App-Token": applicationToken
|
|
1250
|
+
},
|
|
1251
|
+
signal: controller.signal
|
|
1252
|
+
});
|
|
1253
|
+
if (!response.ok) {
|
|
1254
|
+
if (response.status === 401 || response.status === 403) {
|
|
1255
|
+
throw new NeoFaceError("Token de aplicação inválido ou expirado", ErrorType.INVALID_TOKEN);
|
|
1256
|
+
}
|
|
1257
|
+
if (response.status === 404) {
|
|
1258
|
+
return false;
|
|
1259
|
+
}
|
|
1260
|
+
throw new NeoFaceError(`Falha ao validar token (${response.status})`, ErrorType.NETWORK);
|
|
1261
|
+
}
|
|
1262
|
+
const data = await response.json();
|
|
1263
|
+
return (data == null ? void 0 : data.is_valid) === true;
|
|
1264
|
+
} catch (error) {
|
|
1265
|
+
if (error instanceof Error) {
|
|
1266
|
+
if (error.name === "AbortError") {
|
|
1267
|
+
throw new NeoFaceError("Tempo de requisição excedido", ErrorType.NETWORK);
|
|
1268
|
+
}
|
|
1269
|
+
if (error instanceof NeoFaceError) {
|
|
1270
|
+
throw error;
|
|
1271
|
+
}
|
|
1272
|
+
throw new NeoFaceError(error.message, ErrorType.NETWORK);
|
|
1273
|
+
}
|
|
1274
|
+
throw new NeoFaceError("Erro desconhecido", ErrorType.NETWORK_ERROR);
|
|
1275
|
+
}
|
|
1276
|
+
};
|
|
1242
1277
|
const completeOnboarding = async (applicationToken, onboardingToken, faceImage, documentImage) => {
|
|
1243
1278
|
ensureSecureContext();
|
|
1244
1279
|
const controller = createTimeoutController();
|
|
@@ -1290,6 +1325,82 @@ const completeOnboarding = async (applicationToken, onboardingToken, faceImage,
|
|
|
1290
1325
|
throw new NeoFaceError("Erro desconhecido", ErrorType.NETWORK_ERROR);
|
|
1291
1326
|
}
|
|
1292
1327
|
};
|
|
1328
|
+
const completeOnboardingWithData = async (applicationToken, onboardingToken, personData, personImages, documentImage) => {
|
|
1329
|
+
ensureSecureContext();
|
|
1330
|
+
if (!personImages || personImages.length === 0) {
|
|
1331
|
+
throw new NeoFaceError("Pelo menos uma imagem da pessoa é obrigatória", ErrorType.VALIDATION_ERROR);
|
|
1332
|
+
}
|
|
1333
|
+
const controller = createTimeoutController();
|
|
1334
|
+
try {
|
|
1335
|
+
const compressedPersonImages = await Promise.all(
|
|
1336
|
+
personImages.map((img) => compressImage(img))
|
|
1337
|
+
);
|
|
1338
|
+
const compressedDocument = await compressImage(documentImage);
|
|
1339
|
+
const formData = new FormData();
|
|
1340
|
+
compressedPersonImages.forEach((img, index) => {
|
|
1341
|
+
formData.append("person_images", img, `person_${index + 1}.jpg`);
|
|
1342
|
+
});
|
|
1343
|
+
formData.append("document_image", compressedDocument, "document.jpg");
|
|
1344
|
+
if (personData.name) {
|
|
1345
|
+
formData.append("name", personData.name);
|
|
1346
|
+
}
|
|
1347
|
+
if (personData.birth_date) {
|
|
1348
|
+
formData.append("birth_date", personData.birth_date);
|
|
1349
|
+
}
|
|
1350
|
+
if (personData.cpf) {
|
|
1351
|
+
formData.append("cpf", personData.cpf);
|
|
1352
|
+
}
|
|
1353
|
+
if (personData.cnpj) {
|
|
1354
|
+
formData.append("cnpj", personData.cnpj);
|
|
1355
|
+
}
|
|
1356
|
+
if (personData.email) {
|
|
1357
|
+
formData.append("email", personData.email);
|
|
1358
|
+
}
|
|
1359
|
+
const response = await fetch(
|
|
1360
|
+
`${API_BASE_URL}/api/v1/onboardings/${encodeURIComponent(onboardingToken)}/complete`,
|
|
1361
|
+
{
|
|
1362
|
+
method: "POST",
|
|
1363
|
+
headers: {
|
|
1364
|
+
"X-App-Token": applicationToken
|
|
1365
|
+
},
|
|
1366
|
+
body: formData,
|
|
1367
|
+
signal: controller.signal
|
|
1368
|
+
}
|
|
1369
|
+
);
|
|
1370
|
+
const data = await response.json();
|
|
1371
|
+
if (!response.ok) {
|
|
1372
|
+
if (response.status === 401 || response.status === 403) {
|
|
1373
|
+
throw new NeoFaceError("Token de aplicação inválido ou expirado", ErrorType.INVALID_TOKEN);
|
|
1374
|
+
}
|
|
1375
|
+
if (response.status === 404) {
|
|
1376
|
+
throw new NeoFaceError("Link de onboarding não encontrado", ErrorType.NOT_FOUND);
|
|
1377
|
+
}
|
|
1378
|
+
throw new NeoFaceError((data == null ? void 0 : data.error) || (data == null ? void 0 : data.message) || "Falha ao concluir onboarding", ErrorType.NETWORK);
|
|
1379
|
+
}
|
|
1380
|
+
return {
|
|
1381
|
+
success: !!data.success,
|
|
1382
|
+
message: data.message || "Onboarding concluído com sucesso",
|
|
1383
|
+
person_id: data.person_id,
|
|
1384
|
+
identity_data_id: data.identity_data_id,
|
|
1385
|
+
confidence_score: data.confidence_score,
|
|
1386
|
+
processing_time: data.processing_time,
|
|
1387
|
+
liveness_passed: data.liveness_passed,
|
|
1388
|
+
document_verified: data.document_verified,
|
|
1389
|
+
person_created: data.person_created
|
|
1390
|
+
};
|
|
1391
|
+
} catch (error) {
|
|
1392
|
+
if (error instanceof Error) {
|
|
1393
|
+
if (error.name === "AbortError") {
|
|
1394
|
+
throw new NeoFaceError("Tempo de requisição excedido", ErrorType.NETWORK);
|
|
1395
|
+
}
|
|
1396
|
+
if (error instanceof NeoFaceError) {
|
|
1397
|
+
throw error;
|
|
1398
|
+
}
|
|
1399
|
+
throw new NeoFaceError(error.message, ErrorType.NETWORK);
|
|
1400
|
+
}
|
|
1401
|
+
throw new NeoFaceError("Erro desconhecido", ErrorType.NETWORK_ERROR);
|
|
1402
|
+
}
|
|
1403
|
+
};
|
|
1293
1404
|
const recognize = async (image, applicationToken) => {
|
|
1294
1405
|
ensureSecureContext();
|
|
1295
1406
|
const controller = createTimeoutController();
|
|
@@ -1332,7 +1443,7 @@ const recognize = async (image, applicationToken) => {
|
|
|
1332
1443
|
throw new NeoFaceError("Unknown error occurred", ErrorType.NETWORK_ERROR);
|
|
1333
1444
|
}
|
|
1334
1445
|
};
|
|
1335
|
-
const loginWithBiometric = async (image, applicationToken) => {
|
|
1446
|
+
const loginWithBiometric$1 = async (image, applicationToken) => {
|
|
1336
1447
|
var _a, _b, _c, _d, _e;
|
|
1337
1448
|
ensureSecureContext();
|
|
1338
1449
|
const controller = createTimeoutController();
|
|
@@ -3216,7 +3327,7 @@ function BiometricRegistrationModal({
|
|
|
3216
3327
|
/* @__PURE__ */ jsxRuntimeExports.jsx("canvas", { ref: canvasRef })
|
|
3217
3328
|
] });
|
|
3218
3329
|
}
|
|
3219
|
-
|
|
3330
|
+
let BiometricCaptureModal$1 = class BiometricCaptureModal2 {
|
|
3220
3331
|
constructor(options) {
|
|
3221
3332
|
__publicField(this, "modal", null);
|
|
3222
3333
|
__publicField(this, "video", null);
|
|
@@ -3715,60 +3826,847 @@ class BiometricCaptureModal {
|
|
|
3715
3826
|
`;
|
|
3716
3827
|
document.head.appendChild(style);
|
|
3717
3828
|
}
|
|
3829
|
+
};
|
|
3830
|
+
function BiometricStatusOverlayComponent({ status, onClose }) {
|
|
3831
|
+
const [isVisible, setIsVisible] = useState(true);
|
|
3832
|
+
useEffect(() => {
|
|
3833
|
+
if (status === "success") {
|
|
3834
|
+
const timer = setTimeout(() => {
|
|
3835
|
+
setIsVisible(false);
|
|
3836
|
+
setTimeout(() => {
|
|
3837
|
+
onClose == null ? void 0 : onClose();
|
|
3838
|
+
}, 300);
|
|
3839
|
+
}, 800);
|
|
3840
|
+
return () => clearTimeout(timer);
|
|
3841
|
+
}
|
|
3842
|
+
}, [status, onClose]);
|
|
3843
|
+
if (!isVisible)
|
|
3844
|
+
return null;
|
|
3845
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
3846
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("style", { children: `
|
|
3847
|
+
@keyframes fadeIn {
|
|
3848
|
+
from { opacity: 0; transform: scale(0.9); }
|
|
3849
|
+
to { opacity: 1; transform: scale(1); }
|
|
3850
|
+
}
|
|
3851
|
+
|
|
3852
|
+
@keyframes fadeOut {
|
|
3853
|
+
from { opacity: 1; transform: scale(1); }
|
|
3854
|
+
to { opacity: 0; transform: scale(0.9); }
|
|
3855
|
+
}
|
|
3856
|
+
|
|
3857
|
+
@keyframes pulse {
|
|
3858
|
+
0%, 100% {
|
|
3859
|
+
transform: scale(1);
|
|
3860
|
+
opacity: 1;
|
|
3861
|
+
}
|
|
3862
|
+
50% {
|
|
3863
|
+
transform: scale(1.1);
|
|
3864
|
+
opacity: 0.9;
|
|
3865
|
+
}
|
|
3866
|
+
}
|
|
3867
|
+
|
|
3868
|
+
@keyframes spin {
|
|
3869
|
+
from { transform: rotate(0deg); }
|
|
3870
|
+
to { transform: rotate(360deg); }
|
|
3871
|
+
}
|
|
3872
|
+
|
|
3873
|
+
@keyframes checkmark {
|
|
3874
|
+
0% {
|
|
3875
|
+
stroke-dashoffset: 100;
|
|
3876
|
+
opacity: 0;
|
|
3877
|
+
}
|
|
3878
|
+
50% {
|
|
3879
|
+
opacity: 1;
|
|
3880
|
+
}
|
|
3881
|
+
100% {
|
|
3882
|
+
stroke-dashoffset: 0;
|
|
3883
|
+
opacity: 1;
|
|
3884
|
+
}
|
|
3885
|
+
}
|
|
3886
|
+
|
|
3887
|
+
@keyframes errorShake {
|
|
3888
|
+
0%, 100% { transform: translateX(0) scale(1); }
|
|
3889
|
+
10%, 30%, 50%, 70%, 90% { transform: translateX(-4px) scale(1); }
|
|
3890
|
+
20%, 40%, 60%, 80% { transform: translateX(4px) scale(1); }
|
|
3891
|
+
}
|
|
3892
|
+
|
|
3893
|
+
@keyframes scaleIn {
|
|
3894
|
+
0% {
|
|
3895
|
+
transform: scale(0);
|
|
3896
|
+
opacity: 0;
|
|
3897
|
+
}
|
|
3898
|
+
50% {
|
|
3899
|
+
transform: scale(1.1);
|
|
3900
|
+
}
|
|
3901
|
+
100% {
|
|
3902
|
+
transform: scale(1);
|
|
3903
|
+
opacity: 1;
|
|
3904
|
+
}
|
|
3905
|
+
}
|
|
3906
|
+
|
|
3907
|
+
.neofaceid-overlay-container {
|
|
3908
|
+
position: fixed;
|
|
3909
|
+
top: 0;
|
|
3910
|
+
left: 0;
|
|
3911
|
+
right: 0;
|
|
3912
|
+
bottom: 0;
|
|
3913
|
+
display: flex;
|
|
3914
|
+
align-items: center;
|
|
3915
|
+
justify-content: center;
|
|
3916
|
+
z-index: 10000;
|
|
3917
|
+
pointer-events: none;
|
|
3918
|
+
animation: fadeIn 0.3s ease-out;
|
|
3919
|
+
}
|
|
3920
|
+
|
|
3921
|
+
.neofaceid-overlay-container.fade-out {
|
|
3922
|
+
animation: fadeOut 0.3s ease-out;
|
|
3923
|
+
}
|
|
3924
|
+
|
|
3925
|
+
.neofaceid-overlay-content {
|
|
3926
|
+
display: flex;
|
|
3927
|
+
flex-direction: column;
|
|
3928
|
+
align-items: center;
|
|
3929
|
+
justify-content: center;
|
|
3930
|
+
gap: 24px;
|
|
3931
|
+
pointer-events: auto;
|
|
3932
|
+
}
|
|
3933
|
+
|
|
3934
|
+
.neofaceid-logo-wrapper {
|
|
3935
|
+
position: relative;
|
|
3936
|
+
width: 120px;
|
|
3937
|
+
height: 120px;
|
|
3938
|
+
display: flex;
|
|
3939
|
+
align-items: center;
|
|
3940
|
+
justify-content: center;
|
|
3941
|
+
}
|
|
3942
|
+
|
|
3943
|
+
.neofaceid-logo {
|
|
3944
|
+
width: 80px;
|
|
3945
|
+
height: 80px;
|
|
3946
|
+
object-fit: contain;
|
|
3947
|
+
filter: drop-shadow(0 4px 12px rgba(124, 58, 237, 0.3));
|
|
3948
|
+
}
|
|
3949
|
+
|
|
3950
|
+
.neofaceid-logo.pulsing {
|
|
3951
|
+
animation: pulse 2s ease-in-out infinite;
|
|
3952
|
+
}
|
|
3953
|
+
|
|
3954
|
+
.neofaceid-status-icon {
|
|
3955
|
+
position: absolute;
|
|
3956
|
+
inset: 0;
|
|
3957
|
+
display: flex;
|
|
3958
|
+
align-items: center;
|
|
3959
|
+
justify-content: center;
|
|
3960
|
+
}
|
|
3961
|
+
|
|
3962
|
+
.neofaceid-success-icon {
|
|
3963
|
+
width: 60px;
|
|
3964
|
+
height: 60px;
|
|
3965
|
+
color: #10b981;
|
|
3966
|
+
animation: scaleIn 0.6s ease-out;
|
|
3967
|
+
}
|
|
3968
|
+
|
|
3969
|
+
.neofaceid-success-icon svg circle {
|
|
3970
|
+
stroke-dasharray: 100;
|
|
3971
|
+
stroke-dashoffset: 100;
|
|
3972
|
+
animation: checkmark 0.6s ease-out 0.2s forwards;
|
|
3973
|
+
}
|
|
3974
|
+
|
|
3975
|
+
.neofaceid-error-icon {
|
|
3976
|
+
width: 60px;
|
|
3977
|
+
height: 60px;
|
|
3978
|
+
color: #ef4444;
|
|
3979
|
+
animation: errorShake 0.5s ease-out, scaleIn 0.4s ease-out;
|
|
3980
|
+
}
|
|
3981
|
+
|
|
3982
|
+
/* Responsive */
|
|
3983
|
+
@media (max-width: 640px) {
|
|
3984
|
+
.neofaceid-logo-wrapper {
|
|
3985
|
+
width: 100px;
|
|
3986
|
+
height: 100px;
|
|
3987
|
+
}
|
|
3988
|
+
|
|
3989
|
+
.neofaceid-logo {
|
|
3990
|
+
width: 70px;
|
|
3991
|
+
height: 70px;
|
|
3992
|
+
}
|
|
3993
|
+
}
|
|
3994
|
+
` }),
|
|
3995
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: `neofaceid-overlay-container ${!isVisible ? "fade-out" : ""}`, children: /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "neofaceid-overlay-content", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "neofaceid-logo-wrapper", children: [
|
|
3996
|
+
(status === "preparing" || status === "verifying") && /* @__PURE__ */ jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment, { children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
3997
|
+
"img",
|
|
3998
|
+
{
|
|
3999
|
+
src: "/public/logo-icone-no-backgorund.png",
|
|
4000
|
+
alt: "NeoFaceId",
|
|
4001
|
+
className: `neofaceid-logo ${status === "verifying" ? "pulsing" : ""}`,
|
|
4002
|
+
onError: (e) => {
|
|
4003
|
+
var _a;
|
|
4004
|
+
const img = e.target;
|
|
4005
|
+
img.style.display = "none";
|
|
4006
|
+
const fallback = document.createElement("div");
|
|
4007
|
+
fallback.innerHTML = `
|
|
4008
|
+
<svg width="80" height="80" viewBox="0 0 80 80" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
4009
|
+
<circle cx="40" cy="40" r="35" fill="#7c3aed" opacity="0.2"/>
|
|
4010
|
+
<path d="M40 20 L40 60 M20 40 L60 40" stroke="#7c3aed" stroke-width="4" stroke-linecap="round"/>
|
|
4011
|
+
</svg>
|
|
4012
|
+
`;
|
|
4013
|
+
fallback.style.cssText = "width: 80px; height: 80px; display: flex; align-items: center; justify-content: center;";
|
|
4014
|
+
(_a = img.parentElement) == null ? void 0 : _a.appendChild(fallback);
|
|
4015
|
+
}
|
|
4016
|
+
}
|
|
4017
|
+
) }),
|
|
4018
|
+
status === "success" && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "neofaceid-status-icon", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
4019
|
+
"svg",
|
|
4020
|
+
{
|
|
4021
|
+
className: "neofaceid-success-icon",
|
|
4022
|
+
viewBox: "0 0 60 60",
|
|
4023
|
+
fill: "none",
|
|
4024
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
4025
|
+
children: [
|
|
4026
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
4027
|
+
"circle",
|
|
4028
|
+
{
|
|
4029
|
+
cx: "30",
|
|
4030
|
+
cy: "30",
|
|
4031
|
+
r: "28",
|
|
4032
|
+
stroke: "currentColor",
|
|
4033
|
+
strokeWidth: "3",
|
|
4034
|
+
fill: "none"
|
|
4035
|
+
}
|
|
4036
|
+
),
|
|
4037
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
4038
|
+
"path",
|
|
4039
|
+
{
|
|
4040
|
+
d: "M15 30 L25 40 L45 20",
|
|
4041
|
+
stroke: "currentColor",
|
|
4042
|
+
strokeWidth: "3",
|
|
4043
|
+
strokeLinecap: "round",
|
|
4044
|
+
strokeLinejoin: "round",
|
|
4045
|
+
fill: "none"
|
|
4046
|
+
}
|
|
4047
|
+
)
|
|
4048
|
+
]
|
|
4049
|
+
}
|
|
4050
|
+
) }),
|
|
4051
|
+
status === "error" && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "neofaceid-status-icon", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
4052
|
+
"svg",
|
|
4053
|
+
{
|
|
4054
|
+
className: "neofaceid-error-icon",
|
|
4055
|
+
viewBox: "0 0 60 60",
|
|
4056
|
+
fill: "none",
|
|
4057
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
4058
|
+
children: [
|
|
4059
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
4060
|
+
"circle",
|
|
4061
|
+
{
|
|
4062
|
+
cx: "30",
|
|
4063
|
+
cy: "30",
|
|
4064
|
+
r: "28",
|
|
4065
|
+
stroke: "currentColor",
|
|
4066
|
+
strokeWidth: "3",
|
|
4067
|
+
fill: "none"
|
|
4068
|
+
}
|
|
4069
|
+
),
|
|
4070
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
4071
|
+
"path",
|
|
4072
|
+
{
|
|
4073
|
+
d: "M20 20 L40 40 M40 20 L20 40",
|
|
4074
|
+
stroke: "currentColor",
|
|
4075
|
+
strokeWidth: "3",
|
|
4076
|
+
strokeLinecap: "round"
|
|
4077
|
+
}
|
|
4078
|
+
)
|
|
4079
|
+
]
|
|
4080
|
+
}
|
|
4081
|
+
) })
|
|
4082
|
+
] }) }) })
|
|
4083
|
+
] });
|
|
3718
4084
|
}
|
|
3719
|
-
class
|
|
3720
|
-
constructor(
|
|
3721
|
-
__publicField(this, "
|
|
3722
|
-
__publicField(this, "
|
|
3723
|
-
this
|
|
4085
|
+
class BiometricStatusOverlay {
|
|
4086
|
+
constructor() {
|
|
4087
|
+
__publicField(this, "container", null);
|
|
4088
|
+
__publicField(this, "root", null);
|
|
4089
|
+
__publicField(this, "currentStatus", "preparing");
|
|
3724
4090
|
}
|
|
3725
4091
|
/**
|
|
3726
|
-
*
|
|
4092
|
+
* Cria e exibe o overlay
|
|
3727
4093
|
*/
|
|
3728
|
-
|
|
3729
|
-
this.
|
|
3730
|
-
|
|
4094
|
+
show(status = "preparing") {
|
|
4095
|
+
if (this.container) {
|
|
4096
|
+
this.updateStatus(status);
|
|
4097
|
+
return;
|
|
4098
|
+
}
|
|
4099
|
+
this.container = document.createElement("div");
|
|
4100
|
+
this.container.id = "neofaceid-status-overlay";
|
|
4101
|
+
document.body.appendChild(this.container);
|
|
4102
|
+
this.root = createRoot(this.container);
|
|
4103
|
+
this.currentStatus = status;
|
|
4104
|
+
this.render();
|
|
3731
4105
|
}
|
|
3732
4106
|
/**
|
|
3733
|
-
*
|
|
4107
|
+
* Atualiza o status do overlay
|
|
3734
4108
|
*/
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
this.
|
|
4109
|
+
updateStatus(status) {
|
|
4110
|
+
this.currentStatus = status;
|
|
4111
|
+
if (this.root) {
|
|
4112
|
+
this.render();
|
|
3739
4113
|
}
|
|
3740
|
-
|
|
3741
|
-
|
|
4114
|
+
}
|
|
4115
|
+
/**
|
|
4116
|
+
* Fecha e remove o overlay
|
|
4117
|
+
*/
|
|
4118
|
+
close() {
|
|
4119
|
+
if (this.container) {
|
|
4120
|
+
if (this.root) {
|
|
4121
|
+
this.root.render(
|
|
4122
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
4123
|
+
BiometricStatusOverlayComponent,
|
|
4124
|
+
{
|
|
4125
|
+
status: this.currentStatus,
|
|
4126
|
+
onClose: () => {
|
|
4127
|
+
if (this.container && document.body.contains(this.container)) {
|
|
4128
|
+
document.body.removeChild(this.container);
|
|
4129
|
+
}
|
|
4130
|
+
this.container = null;
|
|
4131
|
+
this.root = null;
|
|
4132
|
+
}
|
|
4133
|
+
}
|
|
4134
|
+
)
|
|
4135
|
+
);
|
|
4136
|
+
}
|
|
4137
|
+
setTimeout(() => {
|
|
4138
|
+
if (this.container && document.body.contains(this.container)) {
|
|
4139
|
+
document.body.removeChild(this.container);
|
|
4140
|
+
}
|
|
4141
|
+
this.container = null;
|
|
4142
|
+
this.root = null;
|
|
4143
|
+
}, 300);
|
|
3742
4144
|
}
|
|
3743
4145
|
}
|
|
3744
|
-
|
|
3745
|
-
|
|
3746
|
-
|
|
3747
|
-
|
|
3748
|
-
|
|
3749
|
-
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
<input type="password" id="password" placeholder="Senha" required />
|
|
3754
|
-
<button type="submit">Entrar</button>
|
|
3755
|
-
</form>
|
|
3756
|
-
<button class="cancel-button">Cancelar</button>
|
|
3757
|
-
</div>
|
|
3758
|
-
`;
|
|
3759
|
-
const form = this.modalElement.querySelector("#email-password-form");
|
|
3760
|
-
form.addEventListener("submit", this.handleSubmit.bind(this));
|
|
3761
|
-
const cancelButton = this.modalElement.querySelector(".cancel-button");
|
|
3762
|
-
cancelButton.addEventListener("click", this.close.bind(this));
|
|
4146
|
+
/**
|
|
4147
|
+
* Renderiza o componente
|
|
4148
|
+
*/
|
|
4149
|
+
render() {
|
|
4150
|
+
if (!this.root || !this.container)
|
|
4151
|
+
return;
|
|
4152
|
+
this.root.render(
|
|
4153
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(BiometricStatusOverlayComponent, { status: this.currentStatus, onClose: () => this.close() })
|
|
4154
|
+
);
|
|
3763
4155
|
}
|
|
3764
|
-
|
|
3765
|
-
|
|
3766
|
-
|
|
3767
|
-
|
|
3768
|
-
|
|
4156
|
+
}
|
|
4157
|
+
function FallbackPromptComponent({
|
|
4158
|
+
error,
|
|
4159
|
+
onRetry,
|
|
4160
|
+
onCredentials,
|
|
4161
|
+
onCancel
|
|
4162
|
+
}) {
|
|
4163
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
4164
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("style", { children: `
|
|
4165
|
+
@keyframes slideUp {
|
|
4166
|
+
from {
|
|
4167
|
+
opacity: 0;
|
|
4168
|
+
transform: translateY(20px);
|
|
4169
|
+
}
|
|
4170
|
+
to {
|
|
4171
|
+
opacity: 1;
|
|
4172
|
+
transform: translateY(0);
|
|
4173
|
+
}
|
|
4174
|
+
}
|
|
4175
|
+
|
|
4176
|
+
@keyframes fadeIn {
|
|
4177
|
+
from { opacity: 0; }
|
|
4178
|
+
to { opacity: 1; }
|
|
4179
|
+
}
|
|
4180
|
+
|
|
4181
|
+
.neofaceid-fallback-overlay {
|
|
4182
|
+
position: fixed;
|
|
4183
|
+
inset: 0;
|
|
4184
|
+
background: rgba(0, 0, 0, 0.6);
|
|
4185
|
+
backdrop-filter: blur(8px);
|
|
4186
|
+
display: flex;
|
|
4187
|
+
align-items: center;
|
|
4188
|
+
justify-content: center;
|
|
4189
|
+
z-index: 10001;
|
|
4190
|
+
padding: 20px;
|
|
4191
|
+
animation: fadeIn 0.3s ease-out;
|
|
4192
|
+
}
|
|
4193
|
+
|
|
4194
|
+
.neofaceid-fallback-prompt {
|
|
4195
|
+
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
|
|
4196
|
+
border-radius: 20px;
|
|
4197
|
+
padding: 32px;
|
|
4198
|
+
max-width: 400px;
|
|
4199
|
+
width: 100%;
|
|
4200
|
+
box-shadow: 0 24px 48px rgba(0, 0, 0, 0.4), 0 0 0 1px rgba(255, 255, 255, 0.1);
|
|
4201
|
+
animation: slideUp 0.4s ease-out;
|
|
4202
|
+
text-align: center;
|
|
4203
|
+
}
|
|
4204
|
+
|
|
4205
|
+
.neofaceid-fallback-icon {
|
|
4206
|
+
width: 64px;
|
|
4207
|
+
height: 64px;
|
|
4208
|
+
margin: 0 auto 20px;
|
|
4209
|
+
color: #ef4444;
|
|
4210
|
+
animation: scaleIn 0.4s ease-out;
|
|
4211
|
+
}
|
|
4212
|
+
|
|
4213
|
+
@keyframes scaleIn {
|
|
4214
|
+
0% {
|
|
4215
|
+
transform: scale(0);
|
|
4216
|
+
opacity: 0;
|
|
4217
|
+
}
|
|
4218
|
+
50% {
|
|
4219
|
+
transform: scale(1.1);
|
|
4220
|
+
}
|
|
4221
|
+
100% {
|
|
4222
|
+
transform: scale(1);
|
|
4223
|
+
opacity: 1;
|
|
4224
|
+
}
|
|
4225
|
+
}
|
|
4226
|
+
|
|
4227
|
+
.neofaceid-fallback-title {
|
|
4228
|
+
font-size: 22px;
|
|
4229
|
+
font-weight: 700;
|
|
4230
|
+
color: white;
|
|
4231
|
+
margin: 0 0 12px 0;
|
|
4232
|
+
}
|
|
4233
|
+
|
|
4234
|
+
.neofaceid-fallback-message {
|
|
4235
|
+
font-size: 15px;
|
|
4236
|
+
color: rgba(255, 255, 255, 0.7);
|
|
4237
|
+
margin: 0 0 28px 0;
|
|
4238
|
+
line-height: 1.5;
|
|
4239
|
+
}
|
|
4240
|
+
|
|
4241
|
+
.neofaceid-fallback-actions {
|
|
4242
|
+
display: flex;
|
|
4243
|
+
flex-direction: column;
|
|
4244
|
+
gap: 12px;
|
|
4245
|
+
}
|
|
4246
|
+
|
|
4247
|
+
.neofaceid-fallback-button {
|
|
4248
|
+
padding: 14px 24px;
|
|
4249
|
+
border-radius: 12px;
|
|
4250
|
+
border: none;
|
|
4251
|
+
font-size: 16px;
|
|
4252
|
+
font-weight: 600;
|
|
4253
|
+
cursor: pointer;
|
|
4254
|
+
transition: all 0.2s;
|
|
4255
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
4256
|
+
}
|
|
4257
|
+
|
|
4258
|
+
.neofaceid-fallback-button-primary {
|
|
4259
|
+
background: linear-gradient(135deg, #7c3aed 0%, #6d28d9 100%);
|
|
4260
|
+
color: white;
|
|
4261
|
+
box-shadow: 0 4px 16px rgba(124, 58, 237, 0.3);
|
|
4262
|
+
}
|
|
4263
|
+
|
|
4264
|
+
.neofaceid-fallback-button-primary:hover {
|
|
4265
|
+
transform: translateY(-2px);
|
|
4266
|
+
box-shadow: 0 8px 24px rgba(124, 58, 237, 0.4);
|
|
4267
|
+
}
|
|
4268
|
+
|
|
4269
|
+
.neofaceid-fallback-button-primary:active {
|
|
4270
|
+
transform: translateY(0);
|
|
4271
|
+
}
|
|
4272
|
+
|
|
4273
|
+
.neofaceid-fallback-button-secondary {
|
|
4274
|
+
background: rgba(255, 255, 255, 0.1);
|
|
4275
|
+
color: white;
|
|
4276
|
+
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
4277
|
+
}
|
|
4278
|
+
|
|
4279
|
+
.neofaceid-fallback-button-secondary:hover {
|
|
4280
|
+
background: rgba(255, 255, 255, 0.15);
|
|
4281
|
+
border-color: rgba(255, 255, 255, 0.3);
|
|
4282
|
+
}
|
|
4283
|
+
|
|
4284
|
+
.neofaceid-fallback-button-ghost {
|
|
4285
|
+
background: transparent;
|
|
4286
|
+
color: rgba(255, 255, 255, 0.7);
|
|
4287
|
+
padding: 10px;
|
|
4288
|
+
}
|
|
4289
|
+
|
|
4290
|
+
.neofaceid-fallback-button-ghost:hover {
|
|
4291
|
+
color: white;
|
|
4292
|
+
background: rgba(255, 255, 255, 0.05);
|
|
4293
|
+
}
|
|
4294
|
+
|
|
4295
|
+
@media (max-width: 640px) {
|
|
4296
|
+
.neofaceid-fallback-prompt {
|
|
4297
|
+
padding: 24px;
|
|
4298
|
+
border-radius: 16px;
|
|
4299
|
+
}
|
|
4300
|
+
|
|
4301
|
+
.neofaceid-fallback-title {
|
|
4302
|
+
font-size: 20px;
|
|
4303
|
+
}
|
|
4304
|
+
}
|
|
4305
|
+
` }),
|
|
4306
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "neofaceid-fallback-overlay", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "neofaceid-fallback-prompt", children: [
|
|
4307
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "neofaceid-fallback-icon", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("svg", { viewBox: "0 0 64 64", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
|
|
4308
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("circle", { cx: "32", cy: "32", r: "30", stroke: "currentColor", strokeWidth: "3", fill: "none" }),
|
|
4309
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
4310
|
+
"path",
|
|
4311
|
+
{
|
|
4312
|
+
d: "M20 20 L44 44 M44 20 L20 44",
|
|
4313
|
+
stroke: "currentColor",
|
|
4314
|
+
strokeWidth: "3",
|
|
4315
|
+
strokeLinecap: "round"
|
|
4316
|
+
}
|
|
4317
|
+
)
|
|
4318
|
+
] }) }),
|
|
4319
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "neofaceid-fallback-title", children: "Não foi possível reconhecer" }),
|
|
4320
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "neofaceid-fallback-message", children: (error == null ? void 0 : error.message) || "Não conseguimos reconhecer sua face. Tente novamente ou use email e senha." }),
|
|
4321
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "neofaceid-fallback-actions", children: [
|
|
4322
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
4323
|
+
"button",
|
|
4324
|
+
{
|
|
4325
|
+
type: "button",
|
|
4326
|
+
className: "neofaceid-fallback-button neofaceid-fallback-button-primary",
|
|
4327
|
+
onClick: onRetry,
|
|
4328
|
+
children: "Tentar Novamente"
|
|
4329
|
+
}
|
|
4330
|
+
),
|
|
4331
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
4332
|
+
"button",
|
|
4333
|
+
{
|
|
4334
|
+
type: "button",
|
|
4335
|
+
className: "neofaceid-fallback-button neofaceid-fallback-button-secondary",
|
|
4336
|
+
onClick: onCredentials,
|
|
4337
|
+
children: "Entrar com Email e Senha"
|
|
4338
|
+
}
|
|
4339
|
+
),
|
|
4340
|
+
onCancel && /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
4341
|
+
"button",
|
|
4342
|
+
{
|
|
4343
|
+
type: "button",
|
|
4344
|
+
className: "neofaceid-fallback-button neofaceid-fallback-button-ghost",
|
|
4345
|
+
onClick: onCancel,
|
|
4346
|
+
children: "Cancelar"
|
|
4347
|
+
}
|
|
4348
|
+
)
|
|
4349
|
+
] })
|
|
4350
|
+
] }) })
|
|
4351
|
+
] });
|
|
4352
|
+
}
|
|
4353
|
+
class FallbackPrompt {
|
|
4354
|
+
constructor() {
|
|
4355
|
+
__publicField(this, "container", null);
|
|
4356
|
+
__publicField(this, "root", null);
|
|
4357
|
+
}
|
|
4358
|
+
/**
|
|
4359
|
+
* Exibe o prompt de fallback
|
|
4360
|
+
*/
|
|
4361
|
+
show(error, onRetry, onCredentials, onCancel) {
|
|
4362
|
+
if (this.container) {
|
|
4363
|
+
return;
|
|
4364
|
+
}
|
|
4365
|
+
this.container = document.createElement("div");
|
|
4366
|
+
this.container.id = "neofaceid-fallback-prompt";
|
|
4367
|
+
document.body.appendChild(this.container);
|
|
4368
|
+
this.root = createRoot(this.container);
|
|
4369
|
+
this.render(error, onRetry, onCredentials, onCancel);
|
|
4370
|
+
}
|
|
4371
|
+
/**
|
|
4372
|
+
* Fecha o prompt
|
|
4373
|
+
*/
|
|
4374
|
+
close() {
|
|
4375
|
+
if (this.container && document.body.contains(this.container)) {
|
|
4376
|
+
if (this.root) {
|
|
4377
|
+
this.root.unmount();
|
|
4378
|
+
}
|
|
4379
|
+
document.body.removeChild(this.container);
|
|
4380
|
+
this.container = null;
|
|
4381
|
+
this.root = null;
|
|
4382
|
+
}
|
|
4383
|
+
}
|
|
4384
|
+
/**
|
|
4385
|
+
* Renderiza o componente
|
|
4386
|
+
*/
|
|
4387
|
+
render(error, onRetry, onCredentials, onCancel) {
|
|
4388
|
+
if (!this.root || !this.container)
|
|
4389
|
+
return;
|
|
4390
|
+
this.root.render(
|
|
4391
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
4392
|
+
FallbackPromptComponent,
|
|
4393
|
+
{
|
|
4394
|
+
error,
|
|
4395
|
+
onRetry: () => {
|
|
4396
|
+
this.close();
|
|
4397
|
+
onRetry();
|
|
4398
|
+
},
|
|
4399
|
+
onCredentials: () => {
|
|
4400
|
+
this.close();
|
|
4401
|
+
onCredentials();
|
|
4402
|
+
},
|
|
4403
|
+
onCancel: () => {
|
|
4404
|
+
this.close();
|
|
4405
|
+
onCancel == null ? void 0 : onCancel();
|
|
4406
|
+
}
|
|
4407
|
+
}
|
|
4408
|
+
)
|
|
4409
|
+
);
|
|
4410
|
+
}
|
|
4411
|
+
}
|
|
4412
|
+
class EmailPasswordModal {
|
|
4413
|
+
constructor(options) {
|
|
4414
|
+
__publicField(this, "options");
|
|
4415
|
+
__publicField(this, "modalElement", null);
|
|
4416
|
+
this.options = options;
|
|
4417
|
+
}
|
|
4418
|
+
/**
|
|
4419
|
+
* Abre o modal de login com email e senha.
|
|
4420
|
+
*/
|
|
4421
|
+
async open() {
|
|
4422
|
+
this.createModal();
|
|
4423
|
+
document.body.appendChild(this.modalElement);
|
|
4424
|
+
}
|
|
4425
|
+
/**
|
|
4426
|
+
* Fecha o modal.
|
|
4427
|
+
*/
|
|
4428
|
+
close() {
|
|
4429
|
+
if (this.modalElement) {
|
|
4430
|
+
document.body.removeChild(this.modalElement);
|
|
4431
|
+
this.modalElement = null;
|
|
4432
|
+
}
|
|
4433
|
+
if (this.options.onCancel) {
|
|
4434
|
+
this.options.onCancel();
|
|
4435
|
+
}
|
|
4436
|
+
}
|
|
4437
|
+
createModal() {
|
|
4438
|
+
this.addStyles();
|
|
4439
|
+
this.modalElement = document.createElement("div");
|
|
4440
|
+
this.modalElement.className = "neoface-email-password-modal";
|
|
4441
|
+
this.modalElement.innerHTML = `
|
|
4442
|
+
<div class="neoface-email-modal-overlay">
|
|
4443
|
+
<div class="neoface-email-modal-content">
|
|
4444
|
+
<h2>${this.options.title || "Login com Email e Senha"}</h2>
|
|
4445
|
+
<p>${this.options.subtitle || "Informe seus dados para login."}</p>
|
|
4446
|
+
<form id="email-password-form">
|
|
4447
|
+
<input type="email" id="email" placeholder="Email" required />
|
|
4448
|
+
<input type="password" id="password" placeholder="Senha" required />
|
|
4449
|
+
<button type="submit" class="neoface-email-submit-btn">Entrar</button>
|
|
4450
|
+
</form>
|
|
4451
|
+
<button class="neoface-email-cancel-btn">Cancelar</button>
|
|
4452
|
+
|
|
4453
|
+
<!-- Branding -->
|
|
4454
|
+
<div class="neoface-email-branding">
|
|
4455
|
+
<img src="/public/logo-icone-no-backgorund.png" alt="NeoFaceId" class="neoface-email-brand-logo" onerror="this.style.display='none'" />
|
|
4456
|
+
<span>NeoFaceId by</span>
|
|
4457
|
+
<span class="neoface-email-brand-name">OCTA</span>
|
|
4458
|
+
</div>
|
|
4459
|
+
</div>
|
|
4460
|
+
</div>
|
|
4461
|
+
`;
|
|
4462
|
+
const form = this.modalElement.querySelector("#email-password-form");
|
|
4463
|
+
form.addEventListener("submit", this.handleSubmit.bind(this));
|
|
4464
|
+
const cancelButton = this.modalElement.querySelector(
|
|
4465
|
+
".neoface-email-cancel-btn"
|
|
4466
|
+
);
|
|
4467
|
+
cancelButton.addEventListener("click", this.close.bind(this));
|
|
4468
|
+
}
|
|
4469
|
+
addStyles() {
|
|
4470
|
+
const styleId = "neoface-email-password-modal-styles";
|
|
4471
|
+
const existingStyles = document.getElementById(styleId);
|
|
4472
|
+
if (existingStyles) {
|
|
4473
|
+
return;
|
|
4474
|
+
}
|
|
4475
|
+
const style = document.createElement("style");
|
|
4476
|
+
style.id = styleId;
|
|
4477
|
+
style.textContent = `
|
|
4478
|
+
.neoface-email-password-modal {
|
|
4479
|
+
position: fixed;
|
|
4480
|
+
top: 0;
|
|
4481
|
+
left: 0;
|
|
4482
|
+
width: 100%;
|
|
4483
|
+
height: 100%;
|
|
4484
|
+
z-index: 10002;
|
|
4485
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
4486
|
+
}
|
|
4487
|
+
|
|
4488
|
+
.neoface-email-modal-overlay {
|
|
4489
|
+
position: absolute;
|
|
4490
|
+
inset: 0;
|
|
4491
|
+
background: rgba(0, 0, 0, 0.6);
|
|
4492
|
+
backdrop-filter: blur(8px);
|
|
4493
|
+
display: flex;
|
|
4494
|
+
align-items: center;
|
|
4495
|
+
justify-content: center;
|
|
4496
|
+
padding: 20px;
|
|
4497
|
+
animation: fadeIn 0.3s ease-out;
|
|
4498
|
+
}
|
|
4499
|
+
|
|
4500
|
+
@keyframes fadeIn {
|
|
4501
|
+
from { opacity: 0; }
|
|
4502
|
+
to { opacity: 1; }
|
|
4503
|
+
}
|
|
4504
|
+
|
|
4505
|
+
@keyframes slideUp {
|
|
4506
|
+
from {
|
|
4507
|
+
opacity: 0;
|
|
4508
|
+
transform: translateY(20px);
|
|
4509
|
+
}
|
|
4510
|
+
to {
|
|
4511
|
+
opacity: 1;
|
|
4512
|
+
transform: translateY(0);
|
|
4513
|
+
}
|
|
4514
|
+
}
|
|
4515
|
+
|
|
4516
|
+
.neoface-email-modal-content {
|
|
4517
|
+
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
|
|
4518
|
+
border-radius: 20px;
|
|
4519
|
+
padding: 32px;
|
|
4520
|
+
max-width: 400px;
|
|
4521
|
+
width: 100%;
|
|
4522
|
+
box-shadow: 0 24px 48px rgba(0, 0, 0, 0.4), 0 0 0 1px rgba(255, 255, 255, 0.1);
|
|
4523
|
+
animation: slideUp 0.4s ease-out;
|
|
4524
|
+
}
|
|
4525
|
+
|
|
4526
|
+
.neoface-email-modal-content h2 {
|
|
4527
|
+
font-size: 24px;
|
|
4528
|
+
font-weight: 700;
|
|
4529
|
+
color: white;
|
|
4530
|
+
margin: 0 0 12px 0;
|
|
4531
|
+
text-align: center;
|
|
4532
|
+
}
|
|
4533
|
+
|
|
4534
|
+
.neoface-email-modal-content p {
|
|
4535
|
+
font-size: 15px;
|
|
4536
|
+
color: rgba(255, 255, 255, 0.7);
|
|
4537
|
+
margin: 0 0 24px 0;
|
|
4538
|
+
text-align: center;
|
|
4539
|
+
line-height: 1.5;
|
|
4540
|
+
}
|
|
4541
|
+
|
|
4542
|
+
.neoface-email-modal-content form {
|
|
4543
|
+
display: flex;
|
|
4544
|
+
flex-direction: column;
|
|
4545
|
+
gap: 16px;
|
|
4546
|
+
margin-bottom: 16px;
|
|
4547
|
+
}
|
|
4548
|
+
|
|
4549
|
+
.neoface-email-modal-content input {
|
|
4550
|
+
width: 100%;
|
|
4551
|
+
padding: 14px 16px;
|
|
4552
|
+
border: 2px solid rgba(255, 255, 255, 0.2);
|
|
4553
|
+
border-radius: 12px;
|
|
4554
|
+
font-size: 15px;
|
|
4555
|
+
background: rgba(255, 255, 255, 0.1);
|
|
4556
|
+
color: white;
|
|
4557
|
+
font-family: inherit;
|
|
4558
|
+
transition: all 0.2s;
|
|
4559
|
+
box-sizing: border-box;
|
|
4560
|
+
}
|
|
4561
|
+
|
|
4562
|
+
.neoface-email-modal-content input::placeholder {
|
|
4563
|
+
color: rgba(255, 255, 255, 0.5);
|
|
4564
|
+
}
|
|
4565
|
+
|
|
4566
|
+
.neoface-email-modal-content input:focus {
|
|
4567
|
+
outline: none;
|
|
4568
|
+
border-color: #7c3aed;
|
|
4569
|
+
box-shadow: 0 0 0 3px rgba(124, 58, 237, 0.2);
|
|
4570
|
+
background: rgba(255, 255, 255, 0.15);
|
|
4571
|
+
}
|
|
4572
|
+
|
|
4573
|
+
.neoface-email-submit-btn {
|
|
4574
|
+
width: 100%;
|
|
4575
|
+
padding: 14px 24px;
|
|
4576
|
+
border-radius: 12px;
|
|
4577
|
+
border: none;
|
|
4578
|
+
background: linear-gradient(135deg, #7c3aed 0%, #6d28d9 100%);
|
|
4579
|
+
color: white;
|
|
4580
|
+
font-size: 16px;
|
|
4581
|
+
font-weight: 600;
|
|
4582
|
+
cursor: pointer;
|
|
4583
|
+
transition: all 0.2s;
|
|
4584
|
+
box-shadow: 0 4px 16px rgba(124, 58, 237, 0.3);
|
|
4585
|
+
font-family: inherit;
|
|
4586
|
+
}
|
|
4587
|
+
|
|
4588
|
+
.neoface-email-submit-btn:hover {
|
|
4589
|
+
transform: translateY(-2px);
|
|
4590
|
+
box-shadow: 0 8px 24px rgba(124, 58, 237, 0.4);
|
|
4591
|
+
}
|
|
4592
|
+
|
|
4593
|
+
.neoface-email-submit-btn:active {
|
|
4594
|
+
transform: translateY(0);
|
|
4595
|
+
}
|
|
4596
|
+
|
|
4597
|
+
.neoface-email-submit-btn:disabled {
|
|
4598
|
+
opacity: 0.6;
|
|
4599
|
+
cursor: not-allowed;
|
|
4600
|
+
transform: none;
|
|
4601
|
+
}
|
|
4602
|
+
|
|
4603
|
+
.neoface-email-cancel-btn {
|
|
4604
|
+
width: 100%;
|
|
4605
|
+
padding: 12px 24px;
|
|
4606
|
+
border-radius: 12px;
|
|
4607
|
+
border: 1px solid rgba(255, 255, 255, 0.2);
|
|
4608
|
+
background: transparent;
|
|
4609
|
+
color: rgba(255, 255, 255, 0.7);
|
|
4610
|
+
font-size: 15px;
|
|
4611
|
+
font-weight: 600;
|
|
4612
|
+
cursor: pointer;
|
|
4613
|
+
transition: all 0.2s;
|
|
4614
|
+
font-family: inherit;
|
|
4615
|
+
}
|
|
4616
|
+
|
|
4617
|
+
.neoface-email-cancel-btn:hover {
|
|
4618
|
+
background: rgba(255, 255, 255, 0.1);
|
|
4619
|
+
color: white;
|
|
4620
|
+
border-color: rgba(255, 255, 255, 0.3);
|
|
4621
|
+
}
|
|
4622
|
+
|
|
4623
|
+
.neoface-email-branding {
|
|
4624
|
+
display: flex;
|
|
4625
|
+
align-items: center;
|
|
4626
|
+
justify-content: center;
|
|
4627
|
+
gap: 8px;
|
|
4628
|
+
margin-top: 32px;
|
|
4629
|
+
padding-top: 24px;
|
|
4630
|
+
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
|
4631
|
+
font-size: 13px;
|
|
4632
|
+
color: rgba(255, 255, 255, 0.5);
|
|
4633
|
+
}
|
|
4634
|
+
|
|
4635
|
+
.neoface-email-brand-logo {
|
|
4636
|
+
height: 20px;
|
|
4637
|
+
width: auto;
|
|
4638
|
+
}
|
|
4639
|
+
|
|
4640
|
+
.neoface-email-brand-name {
|
|
4641
|
+
font-weight: 600;
|
|
4642
|
+
font-family: 'SF Pro Display', -apple-system, system-ui, sans-serif;
|
|
4643
|
+
color: #ffffff;
|
|
4644
|
+
letter-spacing: 0.3px;
|
|
4645
|
+
}
|
|
4646
|
+
|
|
4647
|
+
@media (max-width: 640px) {
|
|
4648
|
+
.neoface-email-modal-content {
|
|
4649
|
+
padding: 24px;
|
|
4650
|
+
border-radius: 16px;
|
|
4651
|
+
}
|
|
4652
|
+
|
|
4653
|
+
.neoface-email-modal-content h2 {
|
|
4654
|
+
font-size: 20px;
|
|
4655
|
+
}
|
|
4656
|
+
}
|
|
4657
|
+
`;
|
|
4658
|
+
document.head.appendChild(style);
|
|
4659
|
+
}
|
|
4660
|
+
async handleSubmit(event) {
|
|
4661
|
+
event.preventDefault();
|
|
4662
|
+
const emailInput = this.modalElement.querySelector("#email");
|
|
4663
|
+
const passwordInput = this.modalElement.querySelector("#password");
|
|
4664
|
+
const email = emailInput.value.trim();
|
|
3769
4665
|
const password = passwordInput.value.trim();
|
|
3770
4666
|
if (!email || !password) {
|
|
3771
|
-
this.options.onError(
|
|
4667
|
+
this.options.onError(
|
|
4668
|
+
new NeoFaceError("Email e senha são obrigatórios", ErrorType.VALIDATION_ERROR)
|
|
4669
|
+
);
|
|
3772
4670
|
return;
|
|
3773
4671
|
}
|
|
3774
4672
|
try {
|
|
@@ -3780,6 +4678,230 @@ class EmailPasswordModal {
|
|
|
3780
4678
|
}
|
|
3781
4679
|
}
|
|
3782
4680
|
}
|
|
4681
|
+
const MAX_ATTEMPTS = 3;
|
|
4682
|
+
const RETRY_DELAY = 500;
|
|
4683
|
+
async function detectFaceContinuously(video, minDetectionTime = 3e3, detectionInterval = 200) {
|
|
4684
|
+
return new Promise((resolve) => {
|
|
4685
|
+
let faceDetectedCount = 0;
|
|
4686
|
+
let totalChecks = 0;
|
|
4687
|
+
const requiredChecks = Math.ceil(minDetectionTime / detectionInterval);
|
|
4688
|
+
const minSuccessRate = 0.6;
|
|
4689
|
+
let timeoutId = null;
|
|
4690
|
+
let isResolved = false;
|
|
4691
|
+
const finish = (success) => {
|
|
4692
|
+
if (isResolved)
|
|
4693
|
+
return;
|
|
4694
|
+
isResolved = true;
|
|
4695
|
+
if (timeoutId) {
|
|
4696
|
+
clearTimeout(timeoutId);
|
|
4697
|
+
}
|
|
4698
|
+
resolve(success);
|
|
4699
|
+
};
|
|
4700
|
+
const checkFace = async () => {
|
|
4701
|
+
if (isResolved)
|
|
4702
|
+
return;
|
|
4703
|
+
try {
|
|
4704
|
+
const detection = await faceapi.detectSingleFace(
|
|
4705
|
+
video,
|
|
4706
|
+
new faceapi.TinyFaceDetectorOptions({ inputSize: 160, scoreThreshold: 0.4 })
|
|
4707
|
+
);
|
|
4708
|
+
totalChecks++;
|
|
4709
|
+
if (detection) {
|
|
4710
|
+
faceDetectedCount++;
|
|
4711
|
+
}
|
|
4712
|
+
if (totalChecks >= requiredChecks) {
|
|
4713
|
+
const successRate = faceDetectedCount / totalChecks;
|
|
4714
|
+
finish(successRate >= minSuccessRate);
|
|
4715
|
+
return;
|
|
4716
|
+
}
|
|
4717
|
+
timeoutId = window.setTimeout(checkFace, detectionInterval);
|
|
4718
|
+
} catch (error) {
|
|
4719
|
+
totalChecks++;
|
|
4720
|
+
if (totalChecks >= requiredChecks) {
|
|
4721
|
+
const successRate = faceDetectedCount / totalChecks;
|
|
4722
|
+
finish(successRate >= minSuccessRate);
|
|
4723
|
+
} else {
|
|
4724
|
+
timeoutId = window.setTimeout(checkFace, detectionInterval);
|
|
4725
|
+
}
|
|
4726
|
+
}
|
|
4727
|
+
};
|
|
4728
|
+
const safetyTimeout = window.setTimeout(() => {
|
|
4729
|
+
if (!isResolved) {
|
|
4730
|
+
const successRate = totalChecks > 0 ? faceDetectedCount / totalChecks : 0;
|
|
4731
|
+
finish(successRate >= minSuccessRate);
|
|
4732
|
+
}
|
|
4733
|
+
}, minDetectionTime + 1e3);
|
|
4734
|
+
timeoutId = window.setTimeout(() => {
|
|
4735
|
+
checkFace();
|
|
4736
|
+
clearTimeout(safetyTimeout);
|
|
4737
|
+
}, detectionInterval);
|
|
4738
|
+
});
|
|
4739
|
+
}
|
|
4740
|
+
async function attemptLogin(applicationToken, overlay) {
|
|
4741
|
+
overlay.updateStatus("verifying");
|
|
4742
|
+
const video = document.createElement("video");
|
|
4743
|
+
video.style.position = "fixed";
|
|
4744
|
+
video.style.top = "-9999px";
|
|
4745
|
+
video.style.left = "-9999px";
|
|
4746
|
+
video.style.width = "1px";
|
|
4747
|
+
video.style.height = "1px";
|
|
4748
|
+
video.style.opacity = "0";
|
|
4749
|
+
video.setAttribute("autoplay", "");
|
|
4750
|
+
video.setAttribute("muted", "");
|
|
4751
|
+
video.setAttribute("playsinline", "");
|
|
4752
|
+
document.body.appendChild(video);
|
|
4753
|
+
let stream = null;
|
|
4754
|
+
try {
|
|
4755
|
+
stream = await navigator.mediaDevices.getUserMedia({
|
|
4756
|
+
video: {
|
|
4757
|
+
width: { ideal: 640 },
|
|
4758
|
+
height: { ideal: 480 },
|
|
4759
|
+
facingMode: "user"
|
|
4760
|
+
},
|
|
4761
|
+
audio: false
|
|
4762
|
+
});
|
|
4763
|
+
video.srcObject = stream;
|
|
4764
|
+
await video.play();
|
|
4765
|
+
await new Promise((resolve) => {
|
|
4766
|
+
if (video.readyState >= 2) {
|
|
4767
|
+
resolve(void 0);
|
|
4768
|
+
} else {
|
|
4769
|
+
video.onloadedmetadata = () => resolve(void 0);
|
|
4770
|
+
}
|
|
4771
|
+
});
|
|
4772
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
4773
|
+
const faceDetected = await detectFaceContinuously(video, 3e3);
|
|
4774
|
+
if (!faceDetected) {
|
|
4775
|
+
if (stream) {
|
|
4776
|
+
stream.getTracks().forEach((track) => track.stop());
|
|
4777
|
+
}
|
|
4778
|
+
if (video.parentElement) {
|
|
4779
|
+
video.parentElement.removeChild(video);
|
|
4780
|
+
}
|
|
4781
|
+
throw new NeoFaceError("Rosto não detectado. Posicione seu rosto na frente da câmera.", ErrorType.VALIDATION_ERROR);
|
|
4782
|
+
}
|
|
4783
|
+
const canvas = document.createElement("canvas");
|
|
4784
|
+
canvas.width = video.videoWidth;
|
|
4785
|
+
canvas.height = video.videoHeight;
|
|
4786
|
+
const ctx = canvas.getContext("2d");
|
|
4787
|
+
if (!ctx) {
|
|
4788
|
+
if (stream) {
|
|
4789
|
+
stream.getTracks().forEach((track) => track.stop());
|
|
4790
|
+
}
|
|
4791
|
+
if (video.parentElement) {
|
|
4792
|
+
video.parentElement.removeChild(video);
|
|
4793
|
+
}
|
|
4794
|
+
throw new NeoFaceError("Erro ao capturar imagem", ErrorType.CAPTURE_ERROR);
|
|
4795
|
+
}
|
|
4796
|
+
ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
|
|
4797
|
+
if (stream) {
|
|
4798
|
+
stream.getTracks().forEach((track) => track.stop());
|
|
4799
|
+
}
|
|
4800
|
+
if (video.parentElement) {
|
|
4801
|
+
video.parentElement.removeChild(video);
|
|
4802
|
+
}
|
|
4803
|
+
const imageBlob = await new Promise((resolve, reject) => {
|
|
4804
|
+
canvas.toBlob(
|
|
4805
|
+
(blob) => {
|
|
4806
|
+
canvas.remove();
|
|
4807
|
+
if (blob) {
|
|
4808
|
+
resolve(blob);
|
|
4809
|
+
} else {
|
|
4810
|
+
reject(new Error("Failed to capture image"));
|
|
4811
|
+
}
|
|
4812
|
+
},
|
|
4813
|
+
"image/jpeg",
|
|
4814
|
+
0.85
|
|
4815
|
+
);
|
|
4816
|
+
});
|
|
4817
|
+
const result = await loginWithBiometric$1(imageBlob, applicationToken);
|
|
4818
|
+
if (!result.success) {
|
|
4819
|
+
throw new NeoFaceError("Login falhou", ErrorType.LOGIN_FAILED);
|
|
4820
|
+
}
|
|
4821
|
+
return result;
|
|
4822
|
+
} catch (error) {
|
|
4823
|
+
if (stream) {
|
|
4824
|
+
stream.getTracks().forEach((track) => track.stop());
|
|
4825
|
+
}
|
|
4826
|
+
if (video.parentElement) {
|
|
4827
|
+
video.parentElement.removeChild(video);
|
|
4828
|
+
}
|
|
4829
|
+
if (error instanceof NeoFaceError) {
|
|
4830
|
+
throw error;
|
|
4831
|
+
}
|
|
4832
|
+
throw new NeoFaceError(
|
|
4833
|
+
error instanceof Error ? error.message : "Erro desconhecido",
|
|
4834
|
+
ErrorType.UNKNOWN
|
|
4835
|
+
);
|
|
4836
|
+
}
|
|
4837
|
+
}
|
|
4838
|
+
async function executeBiometricLoginFlow(options) {
|
|
4839
|
+
const { applicationToken, onSuccess, onError, onFallbackRequest, onCancel } = options;
|
|
4840
|
+
const overlay = new BiometricStatusOverlay();
|
|
4841
|
+
const fallbackPrompt = new FallbackPrompt();
|
|
4842
|
+
let attempts = 0;
|
|
4843
|
+
let lastError;
|
|
4844
|
+
try {
|
|
4845
|
+
const MODEL_URL = "https://cdn.jsdelivr.net/npm/@vladmandic/face-api/model";
|
|
4846
|
+
await Promise.all([
|
|
4847
|
+
faceapi.nets.tinyFaceDetector.loadFromUri(MODEL_URL),
|
|
4848
|
+
faceapi.nets.faceLandmark68Net.loadFromUri(MODEL_URL)
|
|
4849
|
+
]);
|
|
4850
|
+
} catch (error) {
|
|
4851
|
+
console.warn("Face-api.js models not loaded, continuing without face detection");
|
|
4852
|
+
}
|
|
4853
|
+
overlay.show("preparing");
|
|
4854
|
+
const tryLogin = async () => {
|
|
4855
|
+
try {
|
|
4856
|
+
attempts++;
|
|
4857
|
+
const result = await attemptLogin(applicationToken, overlay);
|
|
4858
|
+
overlay.updateStatus("success");
|
|
4859
|
+
setTimeout(() => {
|
|
4860
|
+
overlay.close();
|
|
4861
|
+
onSuccess(result);
|
|
4862
|
+
}, 800);
|
|
4863
|
+
} catch (error) {
|
|
4864
|
+
lastError = error instanceof NeoFaceError ? error : new NeoFaceError(
|
|
4865
|
+
error instanceof Error ? error.message : "Erro desconhecido",
|
|
4866
|
+
ErrorType.UNKNOWN
|
|
4867
|
+
);
|
|
4868
|
+
overlay.updateStatus("error");
|
|
4869
|
+
if (attempts < MAX_ATTEMPTS) {
|
|
4870
|
+
setTimeout(() => {
|
|
4871
|
+
tryLogin();
|
|
4872
|
+
}, RETRY_DELAY);
|
|
4873
|
+
} else {
|
|
4874
|
+
setTimeout(() => {
|
|
4875
|
+
overlay.close();
|
|
4876
|
+
fallbackPrompt.show(
|
|
4877
|
+
lastError,
|
|
4878
|
+
() => {
|
|
4879
|
+
attempts = 0;
|
|
4880
|
+
overlay.show("preparing");
|
|
4881
|
+
setTimeout(() => tryLogin(), 500);
|
|
4882
|
+
},
|
|
4883
|
+
() => {
|
|
4884
|
+
if (onFallbackRequest) {
|
|
4885
|
+
onFallbackRequest();
|
|
4886
|
+
} else {
|
|
4887
|
+
onError(
|
|
4888
|
+
new NeoFaceError(
|
|
4889
|
+
"Por favor, use email e senha para fazer login",
|
|
4890
|
+
ErrorType.LOGIN_FAILED
|
|
4891
|
+
)
|
|
4892
|
+
);
|
|
4893
|
+
}
|
|
4894
|
+
},
|
|
4895
|
+
onCancel
|
|
4896
|
+
);
|
|
4897
|
+
}, 1e3);
|
|
4898
|
+
}
|
|
4899
|
+
}
|
|
4900
|
+
};
|
|
4901
|
+
setTimeout(() => {
|
|
4902
|
+
tryLogin();
|
|
4903
|
+
}, 300);
|
|
4904
|
+
}
|
|
3783
4905
|
async function startFaceLogin(options) {
|
|
3784
4906
|
try {
|
|
3785
4907
|
const isValidToken = await validateToken(options.applicationToken);
|
|
@@ -3787,25 +4909,23 @@ async function startFaceLogin(options) {
|
|
|
3787
4909
|
options.onError(new NeoFaceError("Token de aplicação inválido", ErrorType.INVALID_TOKEN));
|
|
3788
4910
|
return;
|
|
3789
4911
|
}
|
|
3790
|
-
|
|
3791
|
-
|
|
3792
|
-
onSuccess:
|
|
3793
|
-
try {
|
|
3794
|
-
const imageBlob = await fetch(imageData).then((res) => res.blob());
|
|
3795
|
-
const result = await loginWithBiometric(imageBlob, options.applicationToken);
|
|
3796
|
-
options.onSuccess(result);
|
|
3797
|
-
} catch (error) {
|
|
3798
|
-
options.onError(error);
|
|
3799
|
-
}
|
|
3800
|
-
},
|
|
4912
|
+
await executeBiometricLoginFlow({
|
|
4913
|
+
applicationToken: options.applicationToken,
|
|
4914
|
+
onSuccess: options.onSuccess,
|
|
3801
4915
|
onError: options.onError,
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
|
|
3807
|
-
|
|
3808
|
-
|
|
4916
|
+
onFallbackRequest: options.onFallbackRequest || (() => {
|
|
4917
|
+
const fallbackOptions = {
|
|
4918
|
+
applicationToken: options.applicationToken,
|
|
4919
|
+
onSuccess: options.onSuccess,
|
|
4920
|
+
onError: options.onError,
|
|
4921
|
+
title: "Login Alternativo",
|
|
4922
|
+
subtitle: "Biometria não reconhecida. Por favor, informe email e senha."
|
|
4923
|
+
};
|
|
4924
|
+
const modal = new EmailPasswordModal(fallbackOptions);
|
|
4925
|
+
modal.open();
|
|
4926
|
+
}),
|
|
4927
|
+
onCancel: options.onCancel
|
|
4928
|
+
});
|
|
3809
4929
|
} catch (error) {
|
|
3810
4930
|
options.onError(
|
|
3811
4931
|
new NeoFaceError(
|
|
@@ -4109,8 +5229,8 @@ const biometricDetection = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.
|
|
|
4109
5229
|
initializeBiometricDetection,
|
|
4110
5230
|
isAdvancedDetectionAvailable
|
|
4111
5231
|
}, Symbol.toStringTag, { value: "Module" }));
|
|
4112
|
-
const VERSION = "1.
|
|
4113
|
-
const RELEASE_DATE = "2025-11-
|
|
5232
|
+
const VERSION = "1.4.0";
|
|
5233
|
+
const RELEASE_DATE = "2025-11-23";
|
|
4114
5234
|
class OnboardingCaptureModal {
|
|
4115
5235
|
constructor(options) {
|
|
4116
5236
|
__publicField(this, "overlay", null);
|
|
@@ -4396,20 +5516,25 @@ function startBiometricRegistration(personData, applicationToken, callbacks, opt
|
|
|
4396
5516
|
});
|
|
4397
5517
|
}
|
|
4398
5518
|
export {
|
|
4399
|
-
BiometricCaptureModal,
|
|
5519
|
+
BiometricCaptureModal$1 as BiometricCaptureModal,
|
|
4400
5520
|
BiometricRegistrationModal,
|
|
5521
|
+
BiometricStatusOverlay,
|
|
5522
|
+
EmailPasswordModal,
|
|
4401
5523
|
ErrorType,
|
|
4402
5524
|
FaceCaptureModal,
|
|
5525
|
+
FallbackPrompt,
|
|
4403
5526
|
NeoFaceError,
|
|
4404
5527
|
RELEASE_DATE,
|
|
4405
5528
|
VERSION,
|
|
4406
5529
|
biometricLogin,
|
|
4407
5530
|
biometricLoginWithFallback,
|
|
5531
|
+
completeOnboarding,
|
|
5532
|
+
completeOnboardingWithData,
|
|
4408
5533
|
detectBiometricType,
|
|
4409
5534
|
identifyPerson,
|
|
4410
5535
|
initializeBiometricDetection,
|
|
4411
5536
|
isAdvancedDetectionAvailable,
|
|
4412
|
-
loginWithBiometric,
|
|
5537
|
+
loginWithBiometric$1 as loginWithBiometric,
|
|
4413
5538
|
recognize,
|
|
4414
5539
|
recognizeBiometric,
|
|
4415
5540
|
recognizeByPurpose,
|
|
@@ -4422,6 +5547,7 @@ export {
|
|
|
4422
5547
|
startFaceLogin,
|
|
4423
5548
|
startHandLogin,
|
|
4424
5549
|
startOnboarding,
|
|
5550
|
+
validateOnboardingToken,
|
|
4425
5551
|
validateToken
|
|
4426
5552
|
};
|
|
4427
5553
|
//# sourceMappingURL=neoface-id-sdk.es.js.map
|