@asteby/storefront-react 0.22.0 → 0.24.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/account.cjs +88 -3
- package/dist/account.cjs.map +1 -1
- package/dist/account.d.cts +34 -2
- package/dist/account.d.ts +34 -2
- package/dist/account.js +89 -5
- package/dist/account.js.map +1 -1
- package/dist/{chunk-PPQEUTXJ.js → chunk-5XYQLOE3.js} +64 -8
- package/dist/chunk-5XYQLOE3.js.map +1 -0
- package/dist/{chunk-Q7UTJIKY.js → chunk-D5L7UZIA.js} +25 -4
- package/dist/chunk-D5L7UZIA.js.map +1 -0
- package/dist/fiscal-server.cjs +8 -9
- package/dist/fiscal-server.cjs.map +1 -1
- package/dist/fiscal-server.d.cts +8 -5
- package/dist/fiscal-server.d.ts +8 -5
- package/dist/fiscal-server.js +9 -10
- package/dist/fiscal-server.js.map +1 -1
- package/dist/fiscal.cjs +464 -24
- package/dist/fiscal.cjs.map +1 -1
- package/dist/fiscal.css +103 -0
- package/dist/fiscal.d.cts +120 -11
- package/dist/fiscal.d.ts +120 -11
- package/dist/fiscal.js +326 -23
- package/dist/fiscal.js.map +1 -1
- package/dist/{types-BnvSbV6t.d.cts → types-Cgx_65eM.d.cts} +14 -2
- package/dist/{types-BnvSbV6t.d.ts → types-Cgx_65eM.d.ts} +14 -2
- package/package.json +2 -2
- package/dist/chunk-PPQEUTXJ.js.map +0 -1
- package/dist/chunk-Q7UTJIKY.js.map +0 -1
package/dist/fiscal.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { validateFiscalValues, regimesFor, usesFor, normalizeTaxId, entryLabel, summarizeExtraction, toInvoiceRequest, maskTaxId } from './chunk-
|
|
2
|
-
export { entryLabel, maskTaxId, normalizeTaxId, personTypeByLength, postalCodeProblem, regimesFor, summarizeExtraction, taxIdProblem, toInvoiceRequest, usesFor, validateFiscalValues } from './chunk-
|
|
3
|
-
import {
|
|
4
|
-
|
|
1
|
+
import { personTypeResolver, validateFiscalValues, regimesFor, usesFor, normalizeTaxId, entryLabel, suggestInvoiceUse, summarizeExtraction, toInvoiceRequest, maskTaxId } from './chunk-D5L7UZIA.js';
|
|
2
|
+
export { entryLabel, maskTaxId, normalizeTaxId, personTypeByLength, personTypeFromPatterns, personTypeResolver, postalCodeProblem, regimesFor, suggestInvoiceUse, summarizeExtraction, taxIdProblem, toInvoiceRequest, usesFor, validateFiscalValues } from './chunk-D5L7UZIA.js';
|
|
3
|
+
import { useModalDialog } from './chunk-ZBH7JYU4.js';
|
|
4
|
+
import { fiscalMessages, fill } from './chunk-5XYQLOE3.js';
|
|
5
|
+
export { fiscalMessages } from './chunk-5XYQLOE3.js';
|
|
5
6
|
import { CheckoutField, ErrorSummary } from './chunk-DFGLCIEG.js';
|
|
6
7
|
import { useResolvedLocale } from './chunk-T52H5HL3.js';
|
|
7
8
|
import './chunk-DQBL2DFJ.js';
|
|
8
|
-
import { useId, useState, useRef, useEffect,
|
|
9
|
+
import { useId, useState, useMemo, useRef, useEffect, useCallback } from 'react';
|
|
9
10
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
10
11
|
|
|
11
12
|
var FIELDS = ["tax_id", "legal_name", "postal_code", "tax_regime", "use"];
|
|
@@ -35,7 +36,8 @@ function FiscalIdentityFields({
|
|
|
35
36
|
errors = {},
|
|
36
37
|
namePrefix = "invoice",
|
|
37
38
|
idPrefix,
|
|
38
|
-
personTypeOf,
|
|
39
|
+
personTypeOf: personTypeOverride,
|
|
40
|
+
suggestedUse,
|
|
39
41
|
disabled,
|
|
40
42
|
locale: localeProp,
|
|
41
43
|
messages,
|
|
@@ -50,6 +52,8 @@ function FiscalIdentityFields({
|
|
|
50
52
|
const base = idPrefix ?? `sf-fiscal-${reactId.replace(/:/g, "")}`;
|
|
51
53
|
const [v, setV] = useState({ ...EMPTY, ...values });
|
|
52
54
|
const [shown, setShown] = useState(/* @__PURE__ */ new Set());
|
|
55
|
+
const [autoUse, setAutoUse] = useState(null);
|
|
56
|
+
const personTypeOf = useMemo(() => personTypeResolver(region.fiscal_id, personTypeOverride), [region.fiscal_id, personTypeOverride]);
|
|
53
57
|
const root = useRef(null);
|
|
54
58
|
const valuesKey = JSON.stringify(values ?? null);
|
|
55
59
|
const first = useRef(true);
|
|
@@ -64,10 +68,17 @@ function FiscalIdentityFields({
|
|
|
64
68
|
const set = (patch) => {
|
|
65
69
|
const next = { ...v, ...patch };
|
|
66
70
|
if (patch.tax_regime !== void 0 && next.use && !usesFor(fid, next.tax_regime).some((u) => u.code === next.use)) next.use = "";
|
|
71
|
+
if (patch.tax_regime !== void 0 && !next.use) {
|
|
72
|
+
const suggestion = suggestInvoiceUse(fid, next.tax_regime);
|
|
73
|
+
if (suggestion) {
|
|
74
|
+
next.use = suggestion;
|
|
75
|
+
setAutoUse(suggestion);
|
|
76
|
+
} else setAutoUse(null);
|
|
77
|
+
} else if (patch.use !== void 0) setAutoUse(null);
|
|
67
78
|
setV(next);
|
|
68
79
|
onChange?.(next);
|
|
69
80
|
};
|
|
70
|
-
const person = personTypeOf
|
|
81
|
+
const person = personTypeOf(v.tax_id);
|
|
71
82
|
const taxIdOk = !validateFiscalValues(region, { tax_id: v.tax_id }, personTypeOf).tax_id;
|
|
72
83
|
const problems = useMemo(() => validateFiscalValues(region, v, personTypeOf), [region, v, personTypeOf]);
|
|
73
84
|
const candidates = regimeCandidates?.length ? regimeCandidates : null;
|
|
@@ -235,7 +246,12 @@ function FiscalIdentityFields({
|
|
|
235
246
|
name: name("use"),
|
|
236
247
|
id: `${base}-use`,
|
|
237
248
|
label: m.use,
|
|
238
|
-
hint:
|
|
249
|
+
hint: v.use && (v.use === autoUse || v.use === suggestedUse) ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
250
|
+
/* @__PURE__ */ jsx("span", { className: cx.mark, "data-sf-suggested": true, children: m.suggested }),
|
|
251
|
+
" ",
|
|
252
|
+
"\xB7 ",
|
|
253
|
+
m.useHint
|
|
254
|
+
] }) : m.useHint,
|
|
239
255
|
value: v.use,
|
|
240
256
|
onChange: (e) => set({ use: e.target.value }),
|
|
241
257
|
onBlur: () => touch("use"),
|
|
@@ -254,10 +270,93 @@ function FiscalIdentityFields({
|
|
|
254
270
|
)
|
|
255
271
|
] });
|
|
256
272
|
}
|
|
273
|
+
function cameraSupported() {
|
|
274
|
+
return typeof navigator !== "undefined" && typeof navigator.mediaDevices?.getUserMedia === "function";
|
|
275
|
+
}
|
|
276
|
+
function useCameraQr({ active, onCode, intervalMs = 250 }) {
|
|
277
|
+
const videoRef = useRef(null);
|
|
278
|
+
const [status, setStatus] = useState("idle");
|
|
279
|
+
const streamRef = useRef(null);
|
|
280
|
+
const onCodeRef = useRef(onCode);
|
|
281
|
+
onCodeRef.current = onCode;
|
|
282
|
+
const stop = useCallback(() => {
|
|
283
|
+
for (const track of streamRef.current?.getTracks() ?? []) track.stop();
|
|
284
|
+
streamRef.current = null;
|
|
285
|
+
const v = videoRef.current;
|
|
286
|
+
if (v) v.srcObject = null;
|
|
287
|
+
setStatus((s) => s === "denied" || s === "unsupported" ? s : "idle");
|
|
288
|
+
}, []);
|
|
289
|
+
useEffect(() => {
|
|
290
|
+
if (!active) return;
|
|
291
|
+
if (!cameraSupported()) {
|
|
292
|
+
setStatus("unsupported");
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
let cancelled = false;
|
|
296
|
+
let timer;
|
|
297
|
+
let busy = false;
|
|
298
|
+
setStatus("starting");
|
|
299
|
+
const onHidden = () => {
|
|
300
|
+
if (document.visibilityState === "hidden") stop();
|
|
301
|
+
};
|
|
302
|
+
document.addEventListener("visibilitychange", onHidden);
|
|
303
|
+
(async () => {
|
|
304
|
+
let stream;
|
|
305
|
+
try {
|
|
306
|
+
stream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: { ideal: "environment" } }, audio: false });
|
|
307
|
+
} catch {
|
|
308
|
+
if (!cancelled) setStatus("denied");
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
if (cancelled) {
|
|
312
|
+
for (const t of stream.getTracks()) t.stop();
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
streamRef.current = stream;
|
|
316
|
+
const video = videoRef.current;
|
|
317
|
+
if (!video) {
|
|
318
|
+
for (const t of stream.getTracks()) t.stop();
|
|
319
|
+
streamRef.current = null;
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
video.srcObject = stream;
|
|
323
|
+
video.muted = true;
|
|
324
|
+
void video.play?.().catch(() => void 0);
|
|
325
|
+
setStatus("scanning");
|
|
326
|
+
const eng = await import('@asteby/storefront/fiscal-document');
|
|
327
|
+
timer = setInterval(async () => {
|
|
328
|
+
if (busy || cancelled || !videoRef.current || videoRef.current.readyState < 2) return;
|
|
329
|
+
busy = true;
|
|
330
|
+
try {
|
|
331
|
+
const text = await eng.decodeImageQr(videoRef.current);
|
|
332
|
+
if (text && !cancelled) {
|
|
333
|
+
cancelled = true;
|
|
334
|
+
clearInterval(timer);
|
|
335
|
+
stop();
|
|
336
|
+
onCodeRef.current(text);
|
|
337
|
+
}
|
|
338
|
+
} catch {
|
|
339
|
+
} finally {
|
|
340
|
+
busy = false;
|
|
341
|
+
}
|
|
342
|
+
}, intervalMs);
|
|
343
|
+
})();
|
|
344
|
+
return () => {
|
|
345
|
+
cancelled = true;
|
|
346
|
+
if (timer) clearInterval(timer);
|
|
347
|
+
document.removeEventListener("visibilitychange", onHidden);
|
|
348
|
+
for (const t of streamRef.current?.getTracks() ?? []) t.stop();
|
|
349
|
+
streamRef.current = null;
|
|
350
|
+
if (videoRef.current) videoRef.current.srcObject = null;
|
|
351
|
+
};
|
|
352
|
+
}, [active, intervalMs, stop]);
|
|
353
|
+
return { videoRef, status, stop };
|
|
354
|
+
}
|
|
257
355
|
var isPdf = (f, accept) => {
|
|
258
356
|
const okType = f.type ? accept.some((a) => a === f.type) || f.type === "application/pdf" : false;
|
|
259
357
|
return okType || /\.pdf$/i.test(f.name);
|
|
260
358
|
};
|
|
359
|
+
var isImage = (f) => f.type.startsWith("image/") || /\.(jpe?g|png|webp|heic|heif)$/i.test(f.name);
|
|
261
360
|
var mib = (bytes) => `${Math.round(bytes / (1024 * 1024) * 10) / 10} MB`;
|
|
262
361
|
function IdentityDocumentReader(props) {
|
|
263
362
|
const { region, workerSrc, onConfirm, onManual, initialValues, data, canSave, serverUpload, personTypeOf, now, namePrefix = "invoice", messages, classNames: cx = {}, confirmLabel } = props;
|
|
@@ -289,14 +388,29 @@ function IdentityDocumentReader(props) {
|
|
|
289
388
|
useEffect(() => () => abort.current?.abort(), []);
|
|
290
389
|
useEffect(() => {
|
|
291
390
|
if (!fromServer) return;
|
|
292
|
-
setValues((cur) =>
|
|
391
|
+
setValues((cur) => {
|
|
392
|
+
const next = { ...cur, ...fromServer.values };
|
|
393
|
+
const suggestion = next.tax_regime && !next.use ? suggestInvoiceUse(region.fiscal_id, next.tax_regime) : null;
|
|
394
|
+
if (suggestion) {
|
|
395
|
+
next.use = suggestion;
|
|
396
|
+
setSuggestedUse(suggestion);
|
|
397
|
+
}
|
|
398
|
+
return next;
|
|
399
|
+
});
|
|
293
400
|
setPhase({ kind: "review", extraction: fromServer, revision: 1 });
|
|
294
401
|
}, [fromServer]);
|
|
295
402
|
useEffect(() => {
|
|
296
403
|
if (phase.kind === "error") errorRef.current?.focus();
|
|
297
404
|
if (phase.kind === "review" && phase.revision > 0) reviewRef.current?.focus();
|
|
298
405
|
}, [phase]);
|
|
299
|
-
const accept = useMemo(() =>
|
|
406
|
+
const accept = useMemo(() => {
|
|
407
|
+
const files = (spec?.accept ?? []).filter((a) => !a.startsWith("image/"));
|
|
408
|
+
return files.length ? files.join(",") : "application/pdf";
|
|
409
|
+
}, [spec]);
|
|
410
|
+
const imageMode = spec?.image_mode && spec.image_mode !== "none" ? spec.image_mode : null;
|
|
411
|
+
const photoInput = useRef(null);
|
|
412
|
+
const [suggestedUse, setSuggestedUse] = useState(null);
|
|
413
|
+
const cameraActive = phase.kind === "camera";
|
|
300
414
|
const fieldErrors = useMemo(() => {
|
|
301
415
|
const out = {};
|
|
302
416
|
for (const [k, v] of Object.entries(data?.fieldErrors ?? {})) out[k] = all.server.errors[v] ?? v;
|
|
@@ -313,9 +427,70 @@ function IdentityDocumentReader(props) {
|
|
|
313
427
|
setPhase({ kind: "review", extraction: null, revision: 1 });
|
|
314
428
|
setLive("");
|
|
315
429
|
}, [onManual]);
|
|
430
|
+
const showExtraction = useCallback(
|
|
431
|
+
(sum) => {
|
|
432
|
+
const base = { tax_id: "", legal_name: "", postal_code: "", tax_regime: "", use: "", ...sum.values };
|
|
433
|
+
const suggestion = base.tax_regime ? suggestInvoiceUse(region.fiscal_id, base.tax_regime) : null;
|
|
434
|
+
if (suggestion && !base.use) base.use = suggestion;
|
|
435
|
+
setSuggestedUse(suggestion && base.use === suggestion ? suggestion : null);
|
|
436
|
+
setValues(base);
|
|
437
|
+
setSummary(null);
|
|
438
|
+
setPhase({ kind: "review", extraction: sum, revision: 1 });
|
|
439
|
+
setLive(m.done);
|
|
440
|
+
},
|
|
441
|
+
[region.fiscal_id, m.done]
|
|
442
|
+
);
|
|
443
|
+
const readQr = useCallback(
|
|
444
|
+
async (text) => {
|
|
445
|
+
if (!spec) return;
|
|
446
|
+
if (!text) return setPhase({ kind: "error", code: "no_qr" });
|
|
447
|
+
const eng = await import('@asteby/storefront/fiscal-document');
|
|
448
|
+
const x = eng.extractIdentityDocument("", spec, {
|
|
449
|
+
catalogs: region.fiscal_id ? eng.catalogsFromRegion(region.fiscal_id) : {},
|
|
450
|
+
qr: text,
|
|
451
|
+
...now ? { now: now() } : {}
|
|
452
|
+
});
|
|
453
|
+
if (!x.fields.tax_id) return setPhase({ kind: "error", code: "no_qr" });
|
|
454
|
+
const evidence = eng.buildDocumentEvidence(x, { sha256: null, source: "qr" });
|
|
455
|
+
showExtraction(summarizeExtraction(x, evidence, region.fiscal_id?.regimes.map((r) => r.code)));
|
|
456
|
+
},
|
|
457
|
+
[spec, region, now, showExtraction]
|
|
458
|
+
);
|
|
459
|
+
const readImage = useCallback(
|
|
460
|
+
async (file) => {
|
|
461
|
+
if (!spec) return;
|
|
462
|
+
if (file.size > spec.max_bytes) return setPhase({ kind: "error", code: "too_large", sizeLimit: spec.max_bytes });
|
|
463
|
+
const ctl = new AbortController();
|
|
464
|
+
abort.current = ctl;
|
|
465
|
+
setPhase({ kind: "reading", fileName: file.name });
|
|
466
|
+
setLive(fill(m.reading, { document: documentName }));
|
|
467
|
+
try {
|
|
468
|
+
const eng = await import('@asteby/storefront/fiscal-document');
|
|
469
|
+
const text = await eng.decodeImageQr(file, { maxBytes: spec.max_bytes, signal: ctl.signal });
|
|
470
|
+
if (ctl.signal.aborted) return;
|
|
471
|
+
await readQr(text);
|
|
472
|
+
} catch (err) {
|
|
473
|
+
if (ctl.signal.aborted) return;
|
|
474
|
+
const code = err?.code;
|
|
475
|
+
setLive("");
|
|
476
|
+
setPhase({ kind: "error", code: code === "too_large" ? "too_large" : code === "not_image" ? "not_image" : "unreadable", sizeLimit: spec.max_bytes });
|
|
477
|
+
} finally {
|
|
478
|
+
if (abort.current === ctl) abort.current = null;
|
|
479
|
+
if (photoInput.current) photoInput.current.value = "";
|
|
480
|
+
}
|
|
481
|
+
},
|
|
482
|
+
[spec, documentName, m.reading, readQr]
|
|
483
|
+
);
|
|
484
|
+
const camera = useCameraQr({
|
|
485
|
+
active: cameraActive,
|
|
486
|
+
onCode: (text) => {
|
|
487
|
+
void readQr(text).then(() => setLive(`${m.scan.cameraRead}. ${m.done}`)).catch(() => setPhase({ kind: "error", code: "unreadable" }));
|
|
488
|
+
}
|
|
489
|
+
});
|
|
316
490
|
const read = useCallback(
|
|
317
491
|
async (file) => {
|
|
318
492
|
if (!spec) return;
|
|
493
|
+
if (imageMode && isImage(file)) return readImage(file);
|
|
319
494
|
if (!isPdf(file, spec.accept ?? [])) return setPhase({ kind: "error", code: "not_pdf" });
|
|
320
495
|
if (file.size > spec.max_bytes) return setPhase({ kind: "error", code: "too_large", sizeLimit: spec.max_bytes });
|
|
321
496
|
const ctl = new AbortController();
|
|
@@ -340,10 +515,7 @@ function IdentityDocumentReader(props) {
|
|
|
340
515
|
});
|
|
341
516
|
const evidence = eng.buildDocumentEvidence(x, { sha256, source: "pdf_client" });
|
|
342
517
|
const sum = summarizeExtraction(x, evidence, region.fiscal_id?.regimes.map((r) => r.code));
|
|
343
|
-
|
|
344
|
-
setSummary(null);
|
|
345
|
-
setPhase({ kind: "review", extraction: sum, revision: 1 });
|
|
346
|
-
setLive(m.done);
|
|
518
|
+
showExtraction(sum);
|
|
347
519
|
} catch (err) {
|
|
348
520
|
if (ctl.signal.aborted) return;
|
|
349
521
|
const code = err?.code;
|
|
@@ -358,7 +530,7 @@ function IdentityDocumentReader(props) {
|
|
|
358
530
|
if (fileInput.current) fileInput.current.value = "";
|
|
359
531
|
}
|
|
360
532
|
},
|
|
361
|
-
[spec, workerSrc, region, now, documentName, m.reading,
|
|
533
|
+
[spec, workerSrc, region, now, documentName, m.reading, imageMode, readImage, showExtraction]
|
|
362
534
|
);
|
|
363
535
|
if (!spec) return null;
|
|
364
536
|
const onDrop = (e) => {
|
|
@@ -368,7 +540,7 @@ function IdentityDocumentReader(props) {
|
|
|
368
540
|
if (f) void read(f);
|
|
369
541
|
};
|
|
370
542
|
const confirm = () => {
|
|
371
|
-
const problems = validateFiscalValues(region, values, personTypeOf);
|
|
543
|
+
const problems = validateFiscalValues(region, values, personTypeResolver(region.fiscal_id, personTypeOf));
|
|
372
544
|
const keys = Object.keys(problems);
|
|
373
545
|
if (keys.length) {
|
|
374
546
|
const labels = {
|
|
@@ -433,6 +605,27 @@ function IdentityDocumentReader(props) {
|
|
|
433
605
|
]
|
|
434
606
|
}
|
|
435
607
|
),
|
|
608
|
+
imageMode ? /* @__PURE__ */ jsxs("div", { className: cx.actions, "data-sf-reader-scan": true, children: [
|
|
609
|
+
/* @__PURE__ */ jsx(
|
|
610
|
+
"input",
|
|
611
|
+
{
|
|
612
|
+
ref: photoInput,
|
|
613
|
+
type: "file",
|
|
614
|
+
accept: "image/*",
|
|
615
|
+
capture: "environment",
|
|
616
|
+
hidden: true,
|
|
617
|
+
tabIndex: -1,
|
|
618
|
+
"aria-label": m.scan.photo,
|
|
619
|
+
"data-sf-reader-photo-input": true,
|
|
620
|
+
onChange: (e) => {
|
|
621
|
+
const f = e.target.files?.[0];
|
|
622
|
+
if (f) void readImage(f);
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
),
|
|
626
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: cx.secondary, onClick: () => photoInput.current?.click(), "data-sf-reader-photo": true, children: m.scan.photo }),
|
|
627
|
+
hydrated && cameraSupported() ? /* @__PURE__ */ jsx("button", { type: "button", className: cx.secondary, onClick: () => setPhase({ kind: "camera" }), "data-sf-reader-camera": true, children: m.scan.camera }) : null
|
|
628
|
+
] }) : null,
|
|
436
629
|
/* @__PURE__ */ jsx("p", { id: helpId, className: cx.privacy, "data-sf-reader-privacy": true, children: privacy }),
|
|
437
630
|
serverUpload ? /* @__PURE__ */ jsx("noscript", { children: /* @__PURE__ */ jsx("button", { type: "submit", name: "intent", value: "extract-document", formEncType: "multipart/form-data", formMethod: "post", formNoValidate: true, className: cx.secondary, "data-sf-reader-upload": true, children: m.uploadServer }) }) : null,
|
|
438
631
|
/* @__PURE__ */ jsxs("p", { "data-sf-reader-links": true, children: [
|
|
@@ -449,6 +642,48 @@ function IdentityDocumentReader(props) {
|
|
|
449
642
|
] }),
|
|
450
643
|
/* @__PURE__ */ jsx("button", { type: "button", className: cx.secondary, onClick: cancel, "data-sf-reader-cancel": true, children: m.cancel })
|
|
451
644
|
] });
|
|
645
|
+
const cameraView = phase.kind === "camera" && /* @__PURE__ */ jsxs("div", { className: cx.root, "data-sf-reader": true, "data-state": "camera", children: [
|
|
646
|
+
/* @__PURE__ */ jsx("h3", { "data-sf-reader-title": true, children: m.scan.cameraTitle }),
|
|
647
|
+
/* @__PURE__ */ jsxs("div", { "data-sf-reader-viewfinder": true, children: [
|
|
648
|
+
/* @__PURE__ */ jsx("video", { ref: camera.videoRef, playsInline: true, muted: true, "aria-label": m.scan.cameraGuide, "data-sf-reader-video": true }),
|
|
649
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", "data-sf-reader-frame": true })
|
|
650
|
+
] }),
|
|
651
|
+
/* @__PURE__ */ jsx("p", { className: cx.status, "data-sf-reader-camera-status": camera.status, children: camera.status === "denied" ? m.scan.cameraDenied : camera.status === "unsupported" ? m.scan.cameraUnavailable : camera.status === "starting" ? m.scan.cameraStarting : m.scan.cameraGuide }),
|
|
652
|
+
/* @__PURE__ */ jsx("p", { className: cx.privacy, "data-sf-reader-privacy": true, children: privacy }),
|
|
653
|
+
/* @__PURE__ */ jsxs("div", { className: cx.actions, "data-sf-reader-actions": true, children: [
|
|
654
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: cx.secondary, onClick: () => photoInput.current?.click(), "data-sf-reader-photo": true, children: m.scan.choosePhoto }),
|
|
655
|
+
/* @__PURE__ */ jsx(
|
|
656
|
+
"button",
|
|
657
|
+
{
|
|
658
|
+
type: "button",
|
|
659
|
+
className: cx.primary,
|
|
660
|
+
onClick: () => {
|
|
661
|
+
camera.stop();
|
|
662
|
+
setPhase({ kind: "idle" });
|
|
663
|
+
},
|
|
664
|
+
"data-sf-reader-camera-close": true,
|
|
665
|
+
children: m.scan.closeCamera
|
|
666
|
+
}
|
|
667
|
+
)
|
|
668
|
+
] }),
|
|
669
|
+
/* @__PURE__ */ jsx(
|
|
670
|
+
"input",
|
|
671
|
+
{
|
|
672
|
+
ref: photoInput,
|
|
673
|
+
type: "file",
|
|
674
|
+
accept: "image/*",
|
|
675
|
+
capture: "environment",
|
|
676
|
+
hidden: true,
|
|
677
|
+
tabIndex: -1,
|
|
678
|
+
"aria-label": m.scan.choosePhoto,
|
|
679
|
+
"data-sf-reader-photo-input": true,
|
|
680
|
+
onChange: (e) => {
|
|
681
|
+
const f = e.target.files?.[0];
|
|
682
|
+
if (f) void readImage(f);
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
)
|
|
686
|
+
] });
|
|
452
687
|
const errored = phase.kind === "error" && /* @__PURE__ */ jsxs("div", { className: cx.root, "data-sf-reader": true, "data-state": "error", children: [
|
|
453
688
|
/* @__PURE__ */ jsx("div", { ref: errorRef, role: "alert", tabIndex: -1, className: cx.error, "data-sf-reader-error": phase.code, children: fill(m.errors[phase.code], { size: mib(phase.sizeLimit ?? spec.max_bytes) }) }),
|
|
454
689
|
/* @__PURE__ */ jsxs("div", { className: cx.actions, "data-sf-reader-actions": true, children: [
|
|
@@ -481,6 +716,7 @@ function IdentityDocumentReader(props) {
|
|
|
481
716
|
fromDocument: phase.extraction?.from_document ?? [],
|
|
482
717
|
documentName,
|
|
483
718
|
regimeCandidates: phase.extraction && phase.extraction.regime_candidates.length > 1 ? phase.extraction.regime_candidates : void 0,
|
|
719
|
+
suggestedUse,
|
|
484
720
|
...personTypeOf ? { personTypeOf } : {},
|
|
485
721
|
locale,
|
|
486
722
|
...messages ? { messages } : {},
|
|
@@ -508,6 +744,7 @@ function IdentityDocumentReader(props) {
|
|
|
508
744
|
/* @__PURE__ */ jsx("div", { role: "status", "aria-live": "polite", className: cx.status, "data-sf-reader-live": true, style: visuallyHidden, children: live }),
|
|
509
745
|
phase.kind === "idle" ? idle : null,
|
|
510
746
|
reading || null,
|
|
747
|
+
cameraView || null,
|
|
511
748
|
errored || null,
|
|
512
749
|
review || null
|
|
513
750
|
] });
|
|
@@ -517,7 +754,8 @@ function Warnings({ x, m, cx }) {
|
|
|
517
754
|
const items = [];
|
|
518
755
|
if (x.qr_match === false) items.push(m.warnings.qrMismatch);
|
|
519
756
|
if (x.stale === true && x.age_days !== null) items.push(fill(m.warnings.stale, { days: x.age_days }));
|
|
520
|
-
if (x.
|
|
757
|
+
if (x.evidence.source === "qr") items.push(m.warnings.qrPartial);
|
|
758
|
+
else if (x.regime_candidates.length === 0 && !x.values.tax_regime) items.push(m.warnings.regimeMissing);
|
|
521
759
|
else if (x.missing.some((k) => k !== "tax_regime" && k !== "document_id" && k !== "issued_on")) items.push(m.warnings.fieldsMissing);
|
|
522
760
|
if (!items.length) return null;
|
|
523
761
|
return /* @__PURE__ */ jsx("ul", { className: cx.warning, "data-sf-reader-warnings": true, children: items.map((t) => /* @__PURE__ */ jsxs("li", { "data-sf-reader-warning": true, children: [
|
|
@@ -525,6 +763,46 @@ function Warnings({ x, m, cx }) {
|
|
|
525
763
|
t
|
|
526
764
|
] }, t)) });
|
|
527
765
|
}
|
|
766
|
+
function ScanTaxDocument(props) {
|
|
767
|
+
const { onConfirm, onManual, classNames: cx = {}, messages, confirmLabel, ...reader } = props;
|
|
768
|
+
const locale = useResolvedLocale(props.locale);
|
|
769
|
+
const m = fiscalMessages(locale, messages).reader;
|
|
770
|
+
const spec = props.document ?? props.region.fiscal_id?.documents?.[0];
|
|
771
|
+
const [open, setOpen] = useState(false);
|
|
772
|
+
const titleId = useId();
|
|
773
|
+
const { ref, onBackdropClick } = useModalDialog({ open, onClose: () => setOpen(false) });
|
|
774
|
+
const opener = useRef(null);
|
|
775
|
+
if (!spec) return null;
|
|
776
|
+
const name = props.documentName ?? spec.label.toLowerCase();
|
|
777
|
+
return /* @__PURE__ */ jsxs("div", { className: cx.actions, "data-sf-scan": true, children: [
|
|
778
|
+
/* @__PURE__ */ jsx("button", { ref: opener, type: "button", className: cx.trigger ?? cx.primary, onClick: () => setOpen(true), "aria-haspopup": "dialog", "data-sf-scan-open": true, children: fill(m.scan.open, { document: spec.label }) }),
|
|
779
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: cx.manual ?? cx.secondary, onClick: onManual, "data-sf-scan-manual": true, children: m.scan.manual }),
|
|
780
|
+
open ? /* @__PURE__ */ jsx("dialog", { ref, role: "dialog", "aria-modal": "true", "aria-labelledby": titleId, onClick: onBackdropClick, onClose: () => setOpen(false), className: cx.sheet, "data-sf-scan-sheet": true, children: /* @__PURE__ */ jsxs("div", { className: cx.sheetPanel, "data-sf-scan-panel": true, children: [
|
|
781
|
+
/* @__PURE__ */ jsxs("div", { className: cx.sheetHeader, "data-sf-scan-header": true, children: [
|
|
782
|
+
/* @__PURE__ */ jsx("h2", { id: titleId, className: cx.sheetTitle, "data-sf-scan-title": true, children: fill(m.scan.title, { document: name }) }),
|
|
783
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: cx.sheetClose, onClick: () => setOpen(false), "aria-label": m.scan.close, "data-sf-scan-close": true, "data-sf-autofocus": true, children: /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "\u2715" }) })
|
|
784
|
+
] }),
|
|
785
|
+
/* @__PURE__ */ jsx(
|
|
786
|
+
IdentityDocumentReader,
|
|
787
|
+
{
|
|
788
|
+
...reader,
|
|
789
|
+
locale,
|
|
790
|
+
...messages ? { messages } : {},
|
|
791
|
+
...confirmLabel ? { confirmLabel } : {},
|
|
792
|
+
classNames: cx,
|
|
793
|
+
onConfirm: (result) => {
|
|
794
|
+
setOpen(false);
|
|
795
|
+
onConfirm(result);
|
|
796
|
+
},
|
|
797
|
+
onManual: () => {
|
|
798
|
+
setOpen(false);
|
|
799
|
+
onManual();
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
)
|
|
803
|
+
] }) }) : null
|
|
804
|
+
] });
|
|
805
|
+
}
|
|
528
806
|
var profileValues = (p) => ({
|
|
529
807
|
tax_id: p.tax_id,
|
|
530
808
|
legal_name: p.legal_name,
|
|
@@ -710,8 +988,7 @@ function labelOf(list, code, locale) {
|
|
|
710
988
|
const e = list?.find((x) => x.code === code);
|
|
711
989
|
return e ? `${e.code} \xB7 ${entryLabel(e, locale)}` : code;
|
|
712
990
|
}
|
|
713
|
-
function
|
|
714
|
-
if (!available) return "unavailable";
|
|
991
|
+
function orderState(invoice) {
|
|
715
992
|
if (!invoice) return "not_requested";
|
|
716
993
|
const stage = invoice.stage ?? "";
|
|
717
994
|
const blocked = invoice.blocked_reason ?? "";
|
|
@@ -726,11 +1003,18 @@ function invoiceCardState(invoice, available = true) {
|
|
|
726
1003
|
if (invoice.can_request === false && blocked) return "unavailable";
|
|
727
1004
|
return "not_requested";
|
|
728
1005
|
}
|
|
1006
|
+
function invoiceCardState(invoice, available = true, reason) {
|
|
1007
|
+
if (available) return orderState(invoice);
|
|
1008
|
+
if (reason === "off" || reason === "no_region") return "not_enabled";
|
|
1009
|
+
const own = orderState(invoice);
|
|
1010
|
+
if (invoice && (own !== "not_requested" || invoice.can_request === true || invoice.action === "request")) return own;
|
|
1011
|
+
return reason === "unreachable" ? "unreachable" : "unknown";
|
|
1012
|
+
}
|
|
729
1013
|
function InvoiceStatusCard(props) {
|
|
730
|
-
const { invoice, available = true, onRequest, requestHref, onCorrect, correctHref, onRefresh, refreshMs = 1e4, refreshForMs = 6e4, contact, messages, classNames: cx = {}, headingLevel = 3 } = props;
|
|
1014
|
+
const { invoice, available = true, availability, testMode, windowDays, onRequest, requestHref, onCorrect, correctHref, onRefresh, refreshMs = 1e4, refreshForMs = 6e4, contact, messages, classNames: cx = {}, headingLevel = 3 } = props;
|
|
731
1015
|
const locale = useResolvedLocale(props.locale);
|
|
732
1016
|
const m = fiscalMessages(locale, messages).status;
|
|
733
|
-
const state = invoiceCardState(invoice, available);
|
|
1017
|
+
const state = invoiceCardState(invoice, available, availability);
|
|
734
1018
|
const titleId = `sf-invoice-card-${useId().replace(/:/g, "")}`;
|
|
735
1019
|
const [tired, setTired] = useState(false);
|
|
736
1020
|
const refresh = useRef(onRefresh);
|
|
@@ -763,7 +1047,10 @@ function InvoiceStatusCard(props) {
|
|
|
763
1047
|
switch (state) {
|
|
764
1048
|
case "not_requested": {
|
|
765
1049
|
title = m.title;
|
|
766
|
-
body = /* @__PURE__ */
|
|
1050
|
+
body = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1051
|
+
/* @__PURE__ */ jsx("p", { className: cx.body, children: m.requestHelp }),
|
|
1052
|
+
windowDays && windowDays > 0 ? /* @__PURE__ */ jsx("p", { className: cx.meta, "data-sf-invoice-window": true, children: fill(m.windowNote, { days: windowDays }) }) : null
|
|
1053
|
+
] });
|
|
767
1054
|
const can = invoice?.can_request !== false;
|
|
768
1055
|
if (can && (onRequest || requestHref)) {
|
|
769
1056
|
actions = onRequest ? /* @__PURE__ */ jsx("button", { type: "button", className: cx.primary, onClick: onRequest, "data-sf-invoice-request": true, children: m.request }) : /* @__PURE__ */ jsx("a", { href: requestHref, className: cx.primary, "data-sf-invoice-request": true, children: m.request });
|
|
@@ -821,6 +1108,18 @@ function InvoiceStatusCard(props) {
|
|
|
821
1108
|
title = m.cancelledTitle;
|
|
822
1109
|
body = /* @__PURE__ */ jsx("p", { className: cx.body, children: m.cancelledBody });
|
|
823
1110
|
break;
|
|
1111
|
+
case "not_enabled":
|
|
1112
|
+
case "unknown":
|
|
1113
|
+
case "unreachable": {
|
|
1114
|
+
title = state === "not_enabled" ? m.notEnabledTitle : m.unknownTitle;
|
|
1115
|
+
body = /* @__PURE__ */ jsx("p", { className: cx.body, children: state === "not_enabled" ? m.notEnabledBody : state === "unreachable" ? m.unreachableBody : m.unknownBody });
|
|
1116
|
+
const mail = contact?.url ?? (contact?.email ? `mailto:${contact.email}` : null);
|
|
1117
|
+
actions = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1118
|
+
state === "unreachable" && onRefresh ? /* @__PURE__ */ jsx("button", { type: "button", className: cx.secondary, onClick: manualRefresh, "data-sf-invoice-refresh": true, children: m.refresh }) : null,
|
|
1119
|
+
mail ? /* @__PURE__ */ jsx("a", { href: mail, className: cx.secondary, "data-sf-invoice-contact": true, children: m.contact }) : null
|
|
1120
|
+
] });
|
|
1121
|
+
break;
|
|
1122
|
+
}
|
|
824
1123
|
default:
|
|
825
1124
|
title = m.unavailableTitle;
|
|
826
1125
|
body = /* @__PURE__ */ jsx("p", { className: cx.body, children: m.unavailableBody });
|
|
@@ -832,6 +1131,7 @@ function InvoiceStatusCard(props) {
|
|
|
832
1131
|
title
|
|
833
1132
|
] }),
|
|
834
1133
|
/* @__PURE__ */ jsx("div", { "aria-live": live || state === "issued" ? "polite" : void 0, "data-sf-invoice-live": true, children: body }),
|
|
1134
|
+
testMode ? /* @__PURE__ */ jsx("p", { className: cx.meta, "data-sf-invoice-sandbox": true, children: m.sandbox }) : null,
|
|
835
1135
|
actions ? /* @__PURE__ */ jsx("div", { className: cx.actions, "data-sf-invoice-actions": true, children: actions }) : null
|
|
836
1136
|
] });
|
|
837
1137
|
}
|
|
@@ -844,9 +1144,12 @@ var ICON = {
|
|
|
844
1144
|
needs_review: "\u25D0",
|
|
845
1145
|
window_closed: "\u2013",
|
|
846
1146
|
unavailable: "\u2013",
|
|
1147
|
+
not_enabled: "\u2013",
|
|
1148
|
+
unknown: "?",
|
|
1149
|
+
unreachable: "?",
|
|
847
1150
|
cancelled: "\xD7"
|
|
848
1151
|
};
|
|
849
1152
|
|
|
850
|
-
export { FiscalIdentityFields, IdentityDocumentReader, InvoiceStatusCard, InvoiceToggle, invoiceCardState };
|
|
1153
|
+
export { FiscalIdentityFields, IdentityDocumentReader, InvoiceStatusCard, InvoiceToggle, ScanTaxDocument, cameraSupported, invoiceCardState, useCameraQr };
|
|
851
1154
|
//# sourceMappingURL=fiscal.js.map
|
|
852
1155
|
//# sourceMappingURL=fiscal.js.map
|