@asteby/storefront-react 0.15.0 → 0.16.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +78 -0
- package/dist/cart.cjs +266 -10
- package/dist/cart.cjs.map +1 -1
- package/dist/cart.css +14 -0
- package/dist/cart.d.cts +125 -2
- package/dist/cart.d.ts +125 -2
- package/dist/cart.js +260 -11
- package/dist/cart.js.map +1 -1
- package/dist/checkout.cjs +24 -3
- package/dist/checkout.cjs.map +1 -1
- package/dist/checkout.d.cts +7 -5
- package/dist/checkout.d.ts +7 -5
- package/dist/checkout.js +24 -3
- package/dist/checkout.js.map +1 -1
- package/dist/chunk-Q7UTJIKY.js +122 -0
- package/dist/chunk-Q7UTJIKY.js.map +1 -0
- package/dist/fiscal-server.cjs +288 -0
- package/dist/fiscal-server.cjs.map +1 -0
- package/dist/fiscal-server.d.cts +97 -0
- package/dist/fiscal-server.d.ts +97 -0
- package/dist/fiscal-server.js +241 -0
- package/dist/fiscal-server.js.map +1 -0
- package/dist/fiscal.cjs +1345 -0
- package/dist/fiscal.cjs.map +1 -0
- package/dist/fiscal.css +343 -0
- package/dist/fiscal.d.cts +313 -0
- package/dist/fiscal.d.ts +313 -0
- package/dist/fiscal.js +1106 -0
- package/dist/fiscal.js.map +1 -0
- package/dist/types-BnvSbV6t.d.cts +68 -0
- package/dist/types-BnvSbV6t.d.ts +68 -0
- package/package.json +23 -2
package/dist/fiscal.js
ADDED
|
@@ -0,0 +1,1106 @@
|
|
|
1
|
+
import { validateFiscalValues, regimesFor, usesFor, normalizeTaxId, entryLabel, summarizeExtraction, toInvoiceRequest, maskTaxId } from './chunk-Q7UTJIKY.js';
|
|
2
|
+
export { entryLabel, maskTaxId, normalizeTaxId, personTypeByLength, postalCodeProblem, regimesFor, summarizeExtraction, taxIdProblem, toInvoiceRequest, usesFor, validateFiscalValues } from './chunk-Q7UTJIKY.js';
|
|
3
|
+
import { useResolvedLocale } from './chunk-T52H5HL3.js';
|
|
4
|
+
import './chunk-DQBL2DFJ.js';
|
|
5
|
+
import { CheckoutField, ErrorSummary } from './chunk-DFGLCIEG.js';
|
|
6
|
+
import { useId, useState, useRef, useEffect, useMemo, useCallback } from 'react';
|
|
7
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
8
|
+
|
|
9
|
+
// src/fiscal/messages.ts
|
|
10
|
+
var ES = {
|
|
11
|
+
fields: {
|
|
12
|
+
legend: "Datos fiscales",
|
|
13
|
+
taxId: "RFC",
|
|
14
|
+
taxIdHint: "Se escribe en may\xFAsculas y sin espacios.",
|
|
15
|
+
legalName: "Nombre o raz\xF3n social",
|
|
16
|
+
legalNameHint: "Escr\xEDbelo tal como aparece en tu registro fiscal.",
|
|
17
|
+
postalCode: "C\xF3digo postal fiscal",
|
|
18
|
+
postalCodeHint: "El de tu domicilio fiscal, que puede ser distinto al de entrega.",
|
|
19
|
+
regime: "R\xE9gimen fiscal",
|
|
20
|
+
regimeHint: "Solo mostramos los que aplican a tu tipo de persona.",
|
|
21
|
+
regimeChoose: "Elige tu r\xE9gimen",
|
|
22
|
+
regimeFromDocument: "Tu documento menciona m\xE1s de un r\xE9gimen. Elige el que usar\xE1s para esta factura.",
|
|
23
|
+
use: "Uso de la factura",
|
|
24
|
+
useHint: "Solo mostramos los usos que admite tu r\xE9gimen.",
|
|
25
|
+
useChoose: "Elige el uso",
|
|
26
|
+
fromDocument: "De tu {document}",
|
|
27
|
+
errors: {
|
|
28
|
+
required: "Completa este campo.",
|
|
29
|
+
taxIdFormat: "Revisa el formato: {kind} no v\xE1lido.",
|
|
30
|
+
postalFormat: "Escribe un c\xF3digo postal v\xE1lido, por ejemplo {example}.",
|
|
31
|
+
unknown: "Elige una opci\xF3n de la lista.",
|
|
32
|
+
personType: "Este r\xE9gimen no corresponde a tu tipo de persona.",
|
|
33
|
+
useNotAllowed: "Este uso no est\xE1 disponible con el r\xE9gimen elegido."
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
reader: {
|
|
37
|
+
title: "Sube tu {document}",
|
|
38
|
+
dropHint: "Arrastra el PDF aqu\xED o toca para elegirlo",
|
|
39
|
+
dropActive: "Suelta el archivo para leerlo",
|
|
40
|
+
choose: "Elegir archivo",
|
|
41
|
+
obtain: "Desc\xE1rgala aqu\xED",
|
|
42
|
+
privacyBrowser: "La leemos en tu dispositivo. No guardamos el archivo, solo los datos que confirmes.",
|
|
43
|
+
privacyServer: "La procesamos y la descartamos al terminar. Solo guardamos los datos que confirmes.",
|
|
44
|
+
uploadServer: "Leer mi archivo",
|
|
45
|
+
manual: "Captura tus datos a mano",
|
|
46
|
+
reading: "Leyendo tu {document}\u2026",
|
|
47
|
+
cancel: "Cancelar",
|
|
48
|
+
done: "Listo. Revisa tus datos.",
|
|
49
|
+
reviewTitle: "Revisa tus datos",
|
|
50
|
+
reviewHint: "Puedes corregir cualquier campo antes de continuar.",
|
|
51
|
+
confirm: "Usar estos datos",
|
|
52
|
+
changeFile: "Subir otro archivo",
|
|
53
|
+
saveToAccount: "Guardar en mi cuenta para la pr\xF3xima",
|
|
54
|
+
saveConsent: "Guardaremos estos datos en tu cuenta para facturar m\xE1s r\xE1pido. Puedes borrarlos cuando quieras.",
|
|
55
|
+
warnings: {
|
|
56
|
+
qrMismatch: "El c\xF3digo QR de tu documento no coincide con los datos le\xEDdos. Revisa con calma tu RFC y tu nombre.",
|
|
57
|
+
stale: "Tu documento tiene {days} d\xEDas de emitido. Si algo cambi\xF3 en tu registro, descarga uno nuevo.",
|
|
58
|
+
regimeMissing: "No pudimos detectar tu r\xE9gimen. El\xEDgelo de la lista.",
|
|
59
|
+
fieldsMissing: "No encontramos algunos datos en el documento. Compl\xE9talos para continuar."
|
|
60
|
+
},
|
|
61
|
+
errors: {
|
|
62
|
+
not_pdf: "Ese archivo no es un PDF. Sube el PDF que descargaste o captura tus datos a mano.",
|
|
63
|
+
too_large: "El archivo pesa m\xE1s de {size}. Sube el PDF original o captura tus datos a mano.",
|
|
64
|
+
no_text: "Este PDF parece una imagen escaneada y no podemos leerlo. Captura tus datos a mano.",
|
|
65
|
+
password: "Este PDF tiene contrase\xF1a y no podemos abrirlo. Desc\xE1rgalo de nuevo sin contrase\xF1a o captura tus datos a mano.",
|
|
66
|
+
unreadable: "No pudimos leer este archivo. Int\xE9ntalo con otro o captura tus datos a mano."
|
|
67
|
+
},
|
|
68
|
+
summaryTitleOne: "Revisa 1 campo para continuar",
|
|
69
|
+
summaryTitleMany: "Revisa {n} campos para continuar"
|
|
70
|
+
},
|
|
71
|
+
toggle: {
|
|
72
|
+
label: "Necesito factura (CFDI)",
|
|
73
|
+
payDefault: "Pagar",
|
|
74
|
+
payAndInvoice: "Pagar y facturar",
|
|
75
|
+
profileTitle: "Facturaremos con estos datos",
|
|
76
|
+
change: "Cambiar",
|
|
77
|
+
useOther: "Usar otros datos",
|
|
78
|
+
useSaved: "Usar mis datos guardados",
|
|
79
|
+
manualTitle: "Datos de facturaci\xF3n",
|
|
80
|
+
unavailable: "Esta tienda todav\xEDa no factura en l\xEDnea.",
|
|
81
|
+
privacy: "Usamos estos datos solo para emitir tu factura.",
|
|
82
|
+
fieldsOf: { taxId: "RFC", legalName: "Nombre o raz\xF3n social", postalCode: "C\xF3digo postal fiscal", regime: "R\xE9gimen", use: "Uso" }
|
|
83
|
+
},
|
|
84
|
+
status: {
|
|
85
|
+
title: "Factura",
|
|
86
|
+
request: "Solicitar factura",
|
|
87
|
+
requestHelp: "\xBFNecesitas factura de este pedido?",
|
|
88
|
+
awaitingTitle: "Esperando tu pago",
|
|
89
|
+
awaitingBody: "Cuando se acredite tu pago emitimos la factura. Con OXXO o transferencia puede tardar un poco.",
|
|
90
|
+
processingTitle: "Estamos generando tu factura",
|
|
91
|
+
processingBody: "Suele tardar menos de un minuto. Esta p\xE1gina se actualiza sola.",
|
|
92
|
+
processingSlow: "Sigue en proceso. Puedes cerrar esta p\xE1gina: tu factura aparecer\xE1 aqu\xED cuando est\xE9 lista.",
|
|
93
|
+
refresh: "Actualizar",
|
|
94
|
+
issuedTitle: "Tu factura est\xE1 lista",
|
|
95
|
+
issuedBody: "Desc\xE1rgala cuando la necesites. Los enlaces vencen en 15 minutos; vuelve a abrir esta p\xE1gina para generar nuevos.",
|
|
96
|
+
folio: "Folio fiscal",
|
|
97
|
+
pdf: "Descargar PDF",
|
|
98
|
+
xml: "Descargar XML",
|
|
99
|
+
needsDataTitle: "Faltan datos para tu factura",
|
|
100
|
+
needsDataBody: "No pudimos emitirla con los datos que nos diste.",
|
|
101
|
+
correct: "Corregir y reintentar",
|
|
102
|
+
reviewTitle: "La tienda est\xE1 revisando tu factura",
|
|
103
|
+
reviewBody: "Necesita una revisi\xF3n del negocio. No tienes que hacer nada m\xE1s; si tarda, escr\xEDbenos.",
|
|
104
|
+
contact: "Contactar a la tienda",
|
|
105
|
+
contactBy: "Escr\xEDbenos a {contact} con tu n\xFAmero de pedido.",
|
|
106
|
+
windowTitle: "Ya pas\xF3 el plazo para facturar",
|
|
107
|
+
windowBody: "Este pedido est\xE1 fuera del plazo para pedir factura. Si necesitas ayuda, contacta a la tienda.",
|
|
108
|
+
unavailableTitle: "Factura no disponible",
|
|
109
|
+
unavailableBody: "Esta tienda no emite factura de este pedido. Si la necesitas, cont\xE1ctala.",
|
|
110
|
+
cancelledTitle: "Factura cancelada",
|
|
111
|
+
cancelledBody: "Esta factura fue cancelada. Si necesitas una nueva, contacta a la tienda.",
|
|
112
|
+
reasons: {
|
|
113
|
+
"invoice.tax_id": "El RFC no es v\xE1lido.",
|
|
114
|
+
"invoice.legal_name": "El nombre o la raz\xF3n social no coincide con tu registro.",
|
|
115
|
+
"invoice.tax_regime": "El r\xE9gimen fiscal no es v\xE1lido para tus datos.",
|
|
116
|
+
"invoice.postal_code": "El c\xF3digo postal fiscal no es v\xE1lido.",
|
|
117
|
+
"invoice.use": "El uso de la factura no es v\xE1lido con tu r\xE9gimen.",
|
|
118
|
+
default: "Revisa tus datos fiscales y vuelve a intentarlo."
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
server: {
|
|
122
|
+
errors: {
|
|
123
|
+
validation: "Revisa este campo.",
|
|
124
|
+
required: "Completa este campo.",
|
|
125
|
+
invalid: "El valor no es v\xE1lido."
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
var EN = {
|
|
130
|
+
fields: {
|
|
131
|
+
legend: "Tax details",
|
|
132
|
+
taxId: "Tax ID",
|
|
133
|
+
taxIdHint: "Uppercase, without spaces.",
|
|
134
|
+
legalName: "Legal name",
|
|
135
|
+
legalNameHint: "Write it as it appears in your tax registration.",
|
|
136
|
+
postalCode: "Tax postal code",
|
|
137
|
+
postalCodeHint: "The one of your tax address, which can differ from the delivery address.",
|
|
138
|
+
regime: "Tax regime",
|
|
139
|
+
regimeHint: "We only list the ones that apply to your kind of taxpayer.",
|
|
140
|
+
regimeChoose: "Choose your regime",
|
|
141
|
+
regimeFromDocument: "Your document lists more than one regime. Choose the one for this invoice.",
|
|
142
|
+
use: "Invoice use",
|
|
143
|
+
useHint: "We only list the uses your regime admits.",
|
|
144
|
+
useChoose: "Choose the use",
|
|
145
|
+
fromDocument: "From your {document}",
|
|
146
|
+
errors: {
|
|
147
|
+
required: "Fill in this field.",
|
|
148
|
+
taxIdFormat: "Check the format: not a valid {kind}.",
|
|
149
|
+
postalFormat: "Enter a valid postal code, like {example}.",
|
|
150
|
+
unknown: "Choose an option from the list.",
|
|
151
|
+
personType: "This regime does not match your kind of taxpayer.",
|
|
152
|
+
useNotAllowed: "This use is not available with the chosen regime."
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
reader: {
|
|
156
|
+
title: "Upload your {document}",
|
|
157
|
+
dropHint: "Drag the PDF here or tap to choose it",
|
|
158
|
+
dropActive: "Drop the file to read it",
|
|
159
|
+
choose: "Choose file",
|
|
160
|
+
obtain: "Get it here",
|
|
161
|
+
privacyBrowser: "We read it on your device. We do not keep the file, only the details you confirm.",
|
|
162
|
+
privacyServer: "We process it and discard it when done. We only keep the details you confirm.",
|
|
163
|
+
uploadServer: "Read my file",
|
|
164
|
+
manual: "Enter your details by hand",
|
|
165
|
+
reading: "Reading your {document}\u2026",
|
|
166
|
+
cancel: "Cancel",
|
|
167
|
+
done: "Done. Review your details.",
|
|
168
|
+
reviewTitle: "Review your details",
|
|
169
|
+
reviewHint: "You can fix any field before continuing.",
|
|
170
|
+
confirm: "Use these details",
|
|
171
|
+
changeFile: "Upload another file",
|
|
172
|
+
saveToAccount: "Save to my account for next time",
|
|
173
|
+
saveConsent: "We will save these details to your account to invoice faster. You can delete them any time.",
|
|
174
|
+
warnings: {
|
|
175
|
+
qrMismatch: "The QR code of your document does not match what we read. Check your tax ID and name carefully.",
|
|
176
|
+
stale: "Your document was issued {days} days ago. If something changed in your registration, download a new one.",
|
|
177
|
+
regimeMissing: "We could not detect your regime. Choose it from the list.",
|
|
178
|
+
fieldsMissing: "We did not find some details in the document. Fill them in to continue."
|
|
179
|
+
},
|
|
180
|
+
errors: {
|
|
181
|
+
not_pdf: "That file is not a PDF. Upload the PDF you downloaded or enter your details by hand.",
|
|
182
|
+
too_large: "The file is larger than {size}. Upload the original PDF or enter your details by hand.",
|
|
183
|
+
no_text: "This PDF looks like a scanned image and we cannot read it. Enter your details by hand.",
|
|
184
|
+
password: "This PDF is password protected and we cannot open it. Download it again without a password or enter your details by hand.",
|
|
185
|
+
unreadable: "We could not read this file. Try another one or enter your details by hand."
|
|
186
|
+
},
|
|
187
|
+
summaryTitleOne: "Check 1 field to continue",
|
|
188
|
+
summaryTitleMany: "Check {n} fields to continue"
|
|
189
|
+
},
|
|
190
|
+
toggle: {
|
|
191
|
+
label: "I need an invoice",
|
|
192
|
+
payDefault: "Pay",
|
|
193
|
+
payAndInvoice: "Pay and invoice",
|
|
194
|
+
profileTitle: "We will invoice with these details",
|
|
195
|
+
change: "Change",
|
|
196
|
+
useOther: "Use other details",
|
|
197
|
+
useSaved: "Use my saved details",
|
|
198
|
+
manualTitle: "Invoice details",
|
|
199
|
+
unavailable: "This store does not invoice online yet.",
|
|
200
|
+
privacy: "We only use these details to issue your invoice.",
|
|
201
|
+
fieldsOf: { taxId: "Tax ID", legalName: "Legal name", postalCode: "Tax postal code", regime: "Regime", use: "Use" }
|
|
202
|
+
},
|
|
203
|
+
status: {
|
|
204
|
+
title: "Invoice",
|
|
205
|
+
request: "Request invoice",
|
|
206
|
+
requestHelp: "Do you need an invoice for this order?",
|
|
207
|
+
awaitingTitle: "Waiting for your payment",
|
|
208
|
+
awaitingBody: "We issue the invoice once your payment clears. Cash and bank transfers can take a while.",
|
|
209
|
+
processingTitle: "We are generating your invoice",
|
|
210
|
+
processingBody: "It usually takes under a minute. This page updates by itself.",
|
|
211
|
+
processingSlow: "Still in progress. You can close this page: your invoice will show up here when it is ready.",
|
|
212
|
+
refresh: "Refresh",
|
|
213
|
+
issuedTitle: "Your invoice is ready",
|
|
214
|
+
issuedBody: "Download it whenever you need. The links expire in 15 minutes; reopen this page for new ones.",
|
|
215
|
+
folio: "Tax folio",
|
|
216
|
+
pdf: "Download PDF",
|
|
217
|
+
xml: "Download XML",
|
|
218
|
+
needsDataTitle: "Your invoice needs more details",
|
|
219
|
+
needsDataBody: "We could not issue it with the details you gave us.",
|
|
220
|
+
correct: "Fix and retry",
|
|
221
|
+
reviewTitle: "The store is reviewing your invoice",
|
|
222
|
+
reviewBody: "It needs a review by the business. You do not need to do anything else; if it takes long, write to us.",
|
|
223
|
+
contact: "Contact the store",
|
|
224
|
+
contactBy: "Write to {contact} with your order number.",
|
|
225
|
+
windowTitle: "The invoicing period has ended",
|
|
226
|
+
windowBody: "This order is outside the period to request an invoice. If you need help, contact the store.",
|
|
227
|
+
unavailableTitle: "Invoice not available",
|
|
228
|
+
unavailableBody: "This store does not issue an invoice for this order. If you need one, contact them.",
|
|
229
|
+
cancelledTitle: "Invoice cancelled",
|
|
230
|
+
cancelledBody: "This invoice was cancelled. If you need a new one, contact the store.",
|
|
231
|
+
reasons: {
|
|
232
|
+
"invoice.tax_id": "The tax ID is not valid.",
|
|
233
|
+
"invoice.legal_name": "The legal name does not match your registration.",
|
|
234
|
+
"invoice.tax_regime": "The tax regime is not valid for your details.",
|
|
235
|
+
"invoice.postal_code": "The tax postal code is not valid.",
|
|
236
|
+
"invoice.use": "The invoice use is not valid with your regime.",
|
|
237
|
+
default: "Check your tax details and try again."
|
|
238
|
+
}
|
|
239
|
+
},
|
|
240
|
+
server: {
|
|
241
|
+
errors: {
|
|
242
|
+
validation: "Check this field.",
|
|
243
|
+
required: "Fill in this field.",
|
|
244
|
+
invalid: "The value is not valid."
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
var merge = (base, over) => {
|
|
249
|
+
if (!over) return base;
|
|
250
|
+
const out = { ...base };
|
|
251
|
+
for (const [k, v] of Object.entries(over)) {
|
|
252
|
+
if (v === void 0) continue;
|
|
253
|
+
const b = base[k];
|
|
254
|
+
out[k] = typeof v === "object" && v !== null && !Array.isArray(v) && typeof b === "object" && b !== null ? merge(b, v) : v;
|
|
255
|
+
}
|
|
256
|
+
return out;
|
|
257
|
+
};
|
|
258
|
+
function fiscalMessages(locale, overrides) {
|
|
259
|
+
const base = (locale ?? "es-MX").toLowerCase().startsWith("en") ? EN : ES;
|
|
260
|
+
return overrides ? merge(base, overrides) : base;
|
|
261
|
+
}
|
|
262
|
+
function fill(template, vars) {
|
|
263
|
+
return template.replace(/\{(\w+)\}/g, (m, k) => k in vars ? String(vars[k]) : m);
|
|
264
|
+
}
|
|
265
|
+
var FIELDS = ["tax_id", "legal_name", "postal_code", "tax_regime", "use"];
|
|
266
|
+
var EMPTY = { tax_id: "", legal_name: "", postal_code: "", tax_regime: "", use: "" };
|
|
267
|
+
function problemText(m, region, field, p) {
|
|
268
|
+
const e = m.errors;
|
|
269
|
+
switch (p) {
|
|
270
|
+
case "required":
|
|
271
|
+
return e.required;
|
|
272
|
+
case "format":
|
|
273
|
+
return field === "tax_id" ? fill(e.taxIdFormat, { kind: region.fiscal_id?.label ?? "" }) : fill(e.postalFormat, { example: region.address?.postal_code?.example ?? "" });
|
|
274
|
+
case "person_type":
|
|
275
|
+
return e.personType;
|
|
276
|
+
case "use_not_allowed":
|
|
277
|
+
return e.useNotAllowed;
|
|
278
|
+
default:
|
|
279
|
+
return e.unknown;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
function FiscalIdentityFields({
|
|
283
|
+
region,
|
|
284
|
+
values,
|
|
285
|
+
onChange,
|
|
286
|
+
fromDocument = [],
|
|
287
|
+
documentName,
|
|
288
|
+
regimeCandidates,
|
|
289
|
+
errors = {},
|
|
290
|
+
namePrefix = "invoice",
|
|
291
|
+
idPrefix,
|
|
292
|
+
personTypeOf,
|
|
293
|
+
disabled,
|
|
294
|
+
locale: localeProp,
|
|
295
|
+
messages,
|
|
296
|
+
classNames: cx = {},
|
|
297
|
+
hideLegend
|
|
298
|
+
}) {
|
|
299
|
+
const locale = useResolvedLocale(localeProp);
|
|
300
|
+
const all = fiscalMessages(locale, messages);
|
|
301
|
+
const m = all.fields;
|
|
302
|
+
const fid = region.fiscal_id;
|
|
303
|
+
const reactId = useId();
|
|
304
|
+
const base = idPrefix ?? `sf-fiscal-${reactId.replace(/:/g, "")}`;
|
|
305
|
+
const [v, setV] = useState({ ...EMPTY, ...values });
|
|
306
|
+
const [shown, setShown] = useState(/* @__PURE__ */ new Set());
|
|
307
|
+
const root = useRef(null);
|
|
308
|
+
const valuesKey = JSON.stringify(values ?? null);
|
|
309
|
+
const first = useRef(true);
|
|
310
|
+
useEffect(() => {
|
|
311
|
+
if (first.current) {
|
|
312
|
+
first.current = false;
|
|
313
|
+
return;
|
|
314
|
+
}
|
|
315
|
+
setV({ ...EMPTY, ...values });
|
|
316
|
+
setShown(/* @__PURE__ */ new Set());
|
|
317
|
+
}, [valuesKey]);
|
|
318
|
+
const set = (patch) => {
|
|
319
|
+
const next = { ...v, ...patch };
|
|
320
|
+
if (patch.tax_regime !== void 0 && next.use && !usesFor(fid, next.tax_regime).some((u) => u.code === next.use)) next.use = "";
|
|
321
|
+
setV(next);
|
|
322
|
+
onChange?.(next);
|
|
323
|
+
};
|
|
324
|
+
const person = personTypeOf?.(v.tax_id) ?? null;
|
|
325
|
+
const taxIdOk = !validateFiscalValues(region, { tax_id: v.tax_id }, personTypeOf).tax_id;
|
|
326
|
+
const problems = useMemo(() => validateFiscalValues(region, v, personTypeOf), [region, v, personTypeOf]);
|
|
327
|
+
const candidates = regimeCandidates?.length ? regimeCandidates : null;
|
|
328
|
+
const regimes = useMemo(() => {
|
|
329
|
+
const list = regimesFor(fid, taxIdOk ? person : null);
|
|
330
|
+
if (candidates) return (fid?.regimes ?? []).filter((r) => candidates.includes(r.code));
|
|
331
|
+
return v.tax_regime && !list.some((r) => r.code === v.tax_regime) ? [...list, ...(fid?.regimes ?? []).filter((r) => r.code === v.tax_regime)] : list;
|
|
332
|
+
}, [fid, person, taxIdOk, candidates, v.tax_regime]);
|
|
333
|
+
const uses = useMemo(() => usesFor(fid, v.tax_regime), [fid, v.tax_regime]);
|
|
334
|
+
useEffect(() => {
|
|
335
|
+
const el = root.current;
|
|
336
|
+
if (!el) return;
|
|
337
|
+
for (const f of FIELDS) {
|
|
338
|
+
const ctl = el.querySelector(`[name="${namePrefix}.${f}"]`);
|
|
339
|
+
const p = problems[f];
|
|
340
|
+
ctl?.setCustomValidity?.(p && p !== "required" ? problemText(m, region, f, p) : "");
|
|
341
|
+
}
|
|
342
|
+
});
|
|
343
|
+
const name = (f) => `${namePrefix}.${f}`;
|
|
344
|
+
const err = (f) => {
|
|
345
|
+
const external = errors[name(f)];
|
|
346
|
+
if (external) return external;
|
|
347
|
+
const p = problems[f];
|
|
348
|
+
return shown.has(f) && p && p !== "required" ? problemText(m, region, f, p) : null;
|
|
349
|
+
};
|
|
350
|
+
const touch = (f) => {
|
|
351
|
+
if (v[f] && !shown.has(f)) setShown((s) => new Set(s).add(f));
|
|
352
|
+
};
|
|
353
|
+
const doc = documentName ?? "";
|
|
354
|
+
const mark = (f) => fromDocument.includes(f) && (v[f] || f === "tax_regime" && candidates) ? /* @__PURE__ */ jsxs("span", { className: cx.mark, "data-sf-from-document": true, children: [
|
|
355
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "\u2713 " }),
|
|
356
|
+
fill(m.fromDocument, { document: doc || (locale.toLowerCase().startsWith("en") ? "document" : "documento") })
|
|
357
|
+
] }) : null;
|
|
358
|
+
const hint = (f, text) => {
|
|
359
|
+
const mk = mark(f);
|
|
360
|
+
if (!mk) return text;
|
|
361
|
+
return text ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
362
|
+
mk,
|
|
363
|
+
" \xB7 ",
|
|
364
|
+
text
|
|
365
|
+
] }) : mk;
|
|
366
|
+
};
|
|
367
|
+
const postalExample = region.address?.postal_code?.example;
|
|
368
|
+
const numericPostal = !!postalExample && /^\d+$/.test(postalExample);
|
|
369
|
+
return /* @__PURE__ */ jsxs("fieldset", { ref: root, className: cx.fieldset, disabled, "data-sf-fiscal-fields": true, style: { border: 0, padding: 0, margin: 0, minInlineSize: 0 }, children: [
|
|
370
|
+
/* @__PURE__ */ jsx("legend", { className: cx.legend, hidden: hideLegend ? true : void 0, style: hideLegend ? void 0 : { padding: 0 }, children: m.legend }),
|
|
371
|
+
/* @__PURE__ */ jsx(
|
|
372
|
+
CheckoutField,
|
|
373
|
+
{
|
|
374
|
+
name: name("tax_id"),
|
|
375
|
+
id: `${base}-tax_id`,
|
|
376
|
+
label: fid?.label || m.taxId,
|
|
377
|
+
hint: hint("tax_id", m.taxIdHint),
|
|
378
|
+
value: v.tax_id,
|
|
379
|
+
onChange: (e) => set({ tax_id: normalizeTaxId(e.target.value) }),
|
|
380
|
+
onBlur: () => touch("tax_id"),
|
|
381
|
+
error: err("tax_id"),
|
|
382
|
+
classNames: cx,
|
|
383
|
+
autoComplete: "off",
|
|
384
|
+
autoCapitalize: "characters",
|
|
385
|
+
spellCheck: false,
|
|
386
|
+
inputMode: "text",
|
|
387
|
+
enterKeyHint: "next",
|
|
388
|
+
maxLength: 20,
|
|
389
|
+
locale
|
|
390
|
+
}
|
|
391
|
+
),
|
|
392
|
+
/* @__PURE__ */ jsx(
|
|
393
|
+
CheckoutField,
|
|
394
|
+
{
|
|
395
|
+
name: name("legal_name"),
|
|
396
|
+
id: `${base}-legal_name`,
|
|
397
|
+
label: m.legalName,
|
|
398
|
+
hint: hint("legal_name", m.legalNameHint),
|
|
399
|
+
value: v.legal_name,
|
|
400
|
+
onChange: (e) => set({ legal_name: e.target.value }),
|
|
401
|
+
onBlur: () => touch("legal_name"),
|
|
402
|
+
error: err("legal_name"),
|
|
403
|
+
classNames: cx,
|
|
404
|
+
autoComplete: "organization",
|
|
405
|
+
enterKeyHint: "next",
|
|
406
|
+
maxLength: 254,
|
|
407
|
+
locale
|
|
408
|
+
}
|
|
409
|
+
),
|
|
410
|
+
/* @__PURE__ */ jsx(
|
|
411
|
+
CheckoutField,
|
|
412
|
+
{
|
|
413
|
+
name: name("postal_code"),
|
|
414
|
+
id: `${base}-postal_code`,
|
|
415
|
+
label: m.postalCode,
|
|
416
|
+
hint: hint("postal_code", m.postalCodeHint),
|
|
417
|
+
value: v.postal_code,
|
|
418
|
+
onChange: (e) => set({ postal_code: e.target.value.trim() }),
|
|
419
|
+
onBlur: () => touch("postal_code"),
|
|
420
|
+
error: err("postal_code"),
|
|
421
|
+
classNames: cx,
|
|
422
|
+
autoComplete: "postal-code",
|
|
423
|
+
inputMode: numericPostal ? "numeric" : "text",
|
|
424
|
+
placeholder: postalExample,
|
|
425
|
+
enterKeyHint: "next",
|
|
426
|
+
maxLength: 12,
|
|
427
|
+
locale
|
|
428
|
+
}
|
|
429
|
+
),
|
|
430
|
+
candidates ? /* @__PURE__ */ jsxs("fieldset", { className: cx.radios, "data-sf-fiscal-regimes": true, "aria-describedby": err("tax_regime") ? `${base}-regime-error` : void 0, style: { border: 0, padding: 0, margin: 0, minInlineSize: 0 }, children: [
|
|
431
|
+
/* @__PURE__ */ jsx("legend", { className: cx.label, "data-sf-label": m.regime, children: m.regime }),
|
|
432
|
+
/* @__PURE__ */ jsxs("p", { className: cx.hint, "data-sf-field-hint": true, children: [
|
|
433
|
+
mark("tax_regime"),
|
|
434
|
+
" \xB7 ",
|
|
435
|
+
m.regimeFromDocument
|
|
436
|
+
] }),
|
|
437
|
+
regimes.map((r, i) => /* @__PURE__ */ jsxs("label", { className: cx.radio, "data-sf-fiscal-radio": true, children: [
|
|
438
|
+
/* @__PURE__ */ jsx(
|
|
439
|
+
"input",
|
|
440
|
+
{
|
|
441
|
+
type: "radio",
|
|
442
|
+
id: i === 0 ? `${base}-tax_regime` : void 0,
|
|
443
|
+
name: name("tax_regime"),
|
|
444
|
+
value: r.code,
|
|
445
|
+
checked: v.tax_regime === r.code,
|
|
446
|
+
required: true,
|
|
447
|
+
onChange: () => set({ tax_regime: r.code })
|
|
448
|
+
}
|
|
449
|
+
),
|
|
450
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
451
|
+
entryLabel(r, locale),
|
|
452
|
+
" ",
|
|
453
|
+
/* @__PURE__ */ jsxs("span", { "data-sf-fiscal-code": true, children: [
|
|
454
|
+
"(",
|
|
455
|
+
r.code,
|
|
456
|
+
")"
|
|
457
|
+
] })
|
|
458
|
+
] })
|
|
459
|
+
] }, r.code)),
|
|
460
|
+
err("tax_regime") ? /* @__PURE__ */ jsx("p", { id: `${base}-regime-error`, className: cx.error, "data-sf-field-error": true, children: err("tax_regime") }) : null
|
|
461
|
+
] }) : /* @__PURE__ */ jsxs(
|
|
462
|
+
CheckoutField,
|
|
463
|
+
{
|
|
464
|
+
as: "select",
|
|
465
|
+
name: name("tax_regime"),
|
|
466
|
+
id: `${base}-tax_regime`,
|
|
467
|
+
label: m.regime,
|
|
468
|
+
hint: hint("tax_regime", m.regimeHint),
|
|
469
|
+
value: v.tax_regime,
|
|
470
|
+
onChange: (e) => set({ tax_regime: e.target.value }),
|
|
471
|
+
onBlur: () => touch("tax_regime"),
|
|
472
|
+
error: err("tax_regime"),
|
|
473
|
+
classNames: cx,
|
|
474
|
+
locale,
|
|
475
|
+
children: [
|
|
476
|
+
/* @__PURE__ */ jsx("option", { value: "", children: m.regimeChoose }),
|
|
477
|
+
regimes.map((r) => /* @__PURE__ */ jsxs("option", { value: r.code, children: [
|
|
478
|
+
r.code,
|
|
479
|
+
" \xB7 ",
|
|
480
|
+
entryLabel(r, locale)
|
|
481
|
+
] }, r.code))
|
|
482
|
+
]
|
|
483
|
+
}
|
|
484
|
+
),
|
|
485
|
+
/* @__PURE__ */ jsxs(
|
|
486
|
+
CheckoutField,
|
|
487
|
+
{
|
|
488
|
+
as: "select",
|
|
489
|
+
name: name("use"),
|
|
490
|
+
id: `${base}-use`,
|
|
491
|
+
label: m.use,
|
|
492
|
+
hint: m.useHint,
|
|
493
|
+
value: v.use,
|
|
494
|
+
onChange: (e) => set({ use: e.target.value }),
|
|
495
|
+
onBlur: () => touch("use"),
|
|
496
|
+
error: err("use"),
|
|
497
|
+
classNames: cx,
|
|
498
|
+
locale,
|
|
499
|
+
children: [
|
|
500
|
+
/* @__PURE__ */ jsx("option", { value: "", children: m.useChoose }),
|
|
501
|
+
uses.map((u) => /* @__PURE__ */ jsxs("option", { value: u.code, children: [
|
|
502
|
+
u.code,
|
|
503
|
+
" \xB7 ",
|
|
504
|
+
entryLabel(u, locale)
|
|
505
|
+
] }, u.code))
|
|
506
|
+
]
|
|
507
|
+
}
|
|
508
|
+
)
|
|
509
|
+
] });
|
|
510
|
+
}
|
|
511
|
+
var isPdf = (f, accept) => {
|
|
512
|
+
const okType = f.type ? accept.some((a) => a === f.type) || f.type === "application/pdf" : false;
|
|
513
|
+
return okType || /\.pdf$/i.test(f.name);
|
|
514
|
+
};
|
|
515
|
+
var mib = (bytes) => `${Math.round(bytes / (1024 * 1024) * 10) / 10} MB`;
|
|
516
|
+
function IdentityDocumentReader(props) {
|
|
517
|
+
const { region, workerSrc, onConfirm, onManual, initialValues, data, canSave, serverUpload, personTypeOf, now, namePrefix = "invoice", messages, classNames: cx = {}, confirmLabel } = props;
|
|
518
|
+
const locale = useResolvedLocale(props.locale);
|
|
519
|
+
const all = fiscalMessages(locale, messages);
|
|
520
|
+
const m = all.reader;
|
|
521
|
+
const spec = props.document ?? region.fiscal_id?.documents?.[0];
|
|
522
|
+
const reactId = useId().replace(/:/g, "");
|
|
523
|
+
const inputId = `sf-reader-${reactId}-file`;
|
|
524
|
+
const helpId = `sf-reader-${reactId}-help`;
|
|
525
|
+
const documentName = props.documentName ?? (spec?.label ?? "document").toLowerCase();
|
|
526
|
+
const fromServer = data?.extraction ?? null;
|
|
527
|
+
const serverError = data && !data.ok && data.error && ["not_pdf", "too_large", "no_text", "password", "unreadable"].includes(data.error.code) ? data.error.code : null;
|
|
528
|
+
const [phase, setPhase] = useState(
|
|
529
|
+
() => fromServer ? { kind: "review", extraction: fromServer, revision: 0 } : serverError ? { kind: "error", code: serverError, sizeLimit: spec?.max_bytes } : { kind: "idle" }
|
|
530
|
+
);
|
|
531
|
+
const [dragging, setDragging] = useState(false);
|
|
532
|
+
const [live, setLive] = useState("");
|
|
533
|
+
const [values, setValues] = useState({ tax_id: "", legal_name: "", postal_code: "", tax_regime: "", use: "", ...initialValues, ...fromServer?.values ?? {} });
|
|
534
|
+
const [save, setSave] = useState(false);
|
|
535
|
+
const [summary, setSummary] = useState(null);
|
|
536
|
+
const [summaryKey, setSummaryKey] = useState(0);
|
|
537
|
+
const [hydrated, setHydrated] = useState(false);
|
|
538
|
+
const abort = useRef(null);
|
|
539
|
+
const fileInput = useRef(null);
|
|
540
|
+
const errorRef = useRef(null);
|
|
541
|
+
const reviewRef = useRef(null);
|
|
542
|
+
useEffect(() => setHydrated(true), []);
|
|
543
|
+
useEffect(() => () => abort.current?.abort(), []);
|
|
544
|
+
useEffect(() => {
|
|
545
|
+
if (!fromServer) return;
|
|
546
|
+
setValues((cur) => ({ ...cur, ...fromServer.values }));
|
|
547
|
+
setPhase({ kind: "review", extraction: fromServer, revision: 1 });
|
|
548
|
+
}, [fromServer]);
|
|
549
|
+
useEffect(() => {
|
|
550
|
+
if (phase.kind === "error") errorRef.current?.focus();
|
|
551
|
+
if (phase.kind === "review" && phase.revision > 0) reviewRef.current?.focus();
|
|
552
|
+
}, [phase]);
|
|
553
|
+
const accept = useMemo(() => spec?.accept?.length ? spec.accept.join(",") : "application/pdf", [spec]);
|
|
554
|
+
const fieldErrors = useMemo(() => {
|
|
555
|
+
const out = {};
|
|
556
|
+
for (const [k, v] of Object.entries(data?.fieldErrors ?? {})) out[k] = all.server.errors[v] ?? v;
|
|
557
|
+
return out;
|
|
558
|
+
}, [data, all.server.errors]);
|
|
559
|
+
const cancel = useCallback(() => {
|
|
560
|
+
abort.current?.abort();
|
|
561
|
+
abort.current = null;
|
|
562
|
+
setPhase({ kind: "idle" });
|
|
563
|
+
setLive("");
|
|
564
|
+
}, []);
|
|
565
|
+
const openManual = useCallback(() => {
|
|
566
|
+
onManual?.();
|
|
567
|
+
setPhase({ kind: "review", extraction: null, revision: 1 });
|
|
568
|
+
setLive("");
|
|
569
|
+
}, [onManual]);
|
|
570
|
+
const read = useCallback(
|
|
571
|
+
async (file) => {
|
|
572
|
+
if (!spec) return;
|
|
573
|
+
if (!isPdf(file, spec.accept ?? [])) return setPhase({ kind: "error", code: "not_pdf" });
|
|
574
|
+
if (file.size > spec.max_bytes) return setPhase({ kind: "error", code: "too_large", sizeLimit: spec.max_bytes });
|
|
575
|
+
const ctl = new AbortController();
|
|
576
|
+
abort.current = ctl;
|
|
577
|
+
setPhase({ kind: "reading", fileName: file.name });
|
|
578
|
+
setLive(fill(m.reading, { document: documentName }));
|
|
579
|
+
try {
|
|
580
|
+
const eng = await import('@asteby/storefront/fiscal-document');
|
|
581
|
+
const { text, sha256 } = await eng.readPdfText(file, {
|
|
582
|
+
maxBytes: spec.max_bytes,
|
|
583
|
+
...spec.max_pages ? { maxPages: spec.max_pages } : {},
|
|
584
|
+
...workerSrc ? { workerSrc } : {},
|
|
585
|
+
signal: ctl.signal
|
|
586
|
+
});
|
|
587
|
+
if (ctl.signal.aborted) return;
|
|
588
|
+
const qr = await eng.decodePdfQr(file, { ...workerSrc ? { workerSrc } : {}, signal: ctl.signal }).catch(() => null);
|
|
589
|
+
if (ctl.signal.aborted) return;
|
|
590
|
+
const x = eng.extractIdentityDocument(text, spec, {
|
|
591
|
+
catalogs: region.fiscal_id ? eng.catalogsFromRegion(region.fiscal_id) : {},
|
|
592
|
+
qr,
|
|
593
|
+
...now ? { now: now() } : {}
|
|
594
|
+
});
|
|
595
|
+
const evidence = eng.buildDocumentEvidence(x, { sha256, source: "pdf_client" });
|
|
596
|
+
const sum = summarizeExtraction(x, evidence, region.fiscal_id?.regimes.map((r) => r.code));
|
|
597
|
+
setValues({ tax_id: "", legal_name: "", postal_code: "", tax_regime: "", use: "", ...sum.values });
|
|
598
|
+
setSummary(null);
|
|
599
|
+
setPhase({ kind: "review", extraction: sum, revision: 1 });
|
|
600
|
+
setLive(m.done);
|
|
601
|
+
} catch (err) {
|
|
602
|
+
if (ctl.signal.aborted) return;
|
|
603
|
+
const code = err?.code;
|
|
604
|
+
setLive("");
|
|
605
|
+
setPhase({
|
|
606
|
+
kind: "error",
|
|
607
|
+
code: code === "not_pdf" || code === "too_large" || code === "no_text" || code === "password" ? code : "unreadable",
|
|
608
|
+
sizeLimit: spec.max_bytes
|
|
609
|
+
});
|
|
610
|
+
} finally {
|
|
611
|
+
if (abort.current === ctl) abort.current = null;
|
|
612
|
+
if (fileInput.current) fileInput.current.value = "";
|
|
613
|
+
}
|
|
614
|
+
},
|
|
615
|
+
[spec, workerSrc, region, now, documentName, m.reading, m.done]
|
|
616
|
+
);
|
|
617
|
+
if (!spec) return null;
|
|
618
|
+
const onDrop = (e) => {
|
|
619
|
+
e.preventDefault();
|
|
620
|
+
setDragging(false);
|
|
621
|
+
const f = e.dataTransfer?.files?.[0];
|
|
622
|
+
if (f) void read(f);
|
|
623
|
+
};
|
|
624
|
+
const confirm = () => {
|
|
625
|
+
const problems = validateFiscalValues(region, values, personTypeOf);
|
|
626
|
+
const keys = Object.keys(problems);
|
|
627
|
+
if (keys.length) {
|
|
628
|
+
const labels = {
|
|
629
|
+
tax_id: region.fiscal_id?.label || all.fields.taxId,
|
|
630
|
+
legal_name: all.fields.legalName,
|
|
631
|
+
postal_code: all.fields.postalCode,
|
|
632
|
+
tax_regime: all.fields.regime,
|
|
633
|
+
use: all.fields.use
|
|
634
|
+
};
|
|
635
|
+
const item = (f) => {
|
|
636
|
+
const p = problems[f];
|
|
637
|
+
const msg = p === "required" ? all.fields.errors.required : p === "format" ? f === "tax_id" ? fill(all.fields.errors.taxIdFormat, { kind: region.fiscal_id?.label ?? "" }) : fill(all.fields.errors.postalFormat, { example: region.address?.postal_code?.example ?? "" }) : p === "person_type" ? all.fields.errors.personType : p === "use_not_allowed" ? all.fields.errors.useNotAllowed : all.fields.errors.unknown;
|
|
638
|
+
return { name: `${namePrefix}.${f}`, id: `sf-reader-${reactId}-${f}`, label: labels[f], message: msg };
|
|
639
|
+
};
|
|
640
|
+
setSummary(keys.map(item));
|
|
641
|
+
setSummaryKey((k) => k + 1);
|
|
642
|
+
return;
|
|
643
|
+
}
|
|
644
|
+
setSummary(null);
|
|
645
|
+
const ev = phase.kind === "review" ? phase.extraction?.evidence ?? null : null;
|
|
646
|
+
onConfirm({ values, evidence: ev, save: !!canSave && save });
|
|
647
|
+
};
|
|
648
|
+
const errorsByName = { ...fieldErrors };
|
|
649
|
+
for (const s of summary ?? []) errorsByName[s.name] = s.message;
|
|
650
|
+
const privacy = serverUpload && !hydrated ? m.privacyServer : m.privacyBrowser;
|
|
651
|
+
const idle = /* @__PURE__ */ jsxs("div", { className: cx.root, "data-sf-reader": true, "data-state": "idle", children: [
|
|
652
|
+
/* @__PURE__ */ jsx("h3", { "data-sf-reader-title": true, children: fill(m.title, { document: documentName }) }),
|
|
653
|
+
spec.help ? /* @__PURE__ */ jsx("p", { "data-sf-reader-help": true, children: spec.help }) : null,
|
|
654
|
+
/* @__PURE__ */ jsxs(
|
|
655
|
+
"label",
|
|
656
|
+
{
|
|
657
|
+
htmlFor: inputId,
|
|
658
|
+
className: dragging ? cx.zoneActive : cx.zone,
|
|
659
|
+
"data-sf-reader-zone": true,
|
|
660
|
+
"data-dragging": dragging ? "" : void 0,
|
|
661
|
+
onDragOver: (e) => {
|
|
662
|
+
e.preventDefault();
|
|
663
|
+
setDragging(true);
|
|
664
|
+
},
|
|
665
|
+
onDragLeave: () => setDragging(false),
|
|
666
|
+
onDrop,
|
|
667
|
+
children: [
|
|
668
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", "data-sf-reader-icon": true, children: "\u2B06" }),
|
|
669
|
+
/* @__PURE__ */ jsx("span", { "data-sf-reader-drop": true, children: dragging ? m.dropActive : m.dropHint }),
|
|
670
|
+
/* @__PURE__ */ jsx("span", { "data-sf-reader-choose": true, children: m.choose }),
|
|
671
|
+
/* @__PURE__ */ jsx(
|
|
672
|
+
"input",
|
|
673
|
+
{
|
|
674
|
+
ref: fileInput,
|
|
675
|
+
id: inputId,
|
|
676
|
+
type: "file",
|
|
677
|
+
name: serverUpload && !hydrated ? "document" : void 0,
|
|
678
|
+
accept,
|
|
679
|
+
"aria-describedby": helpId,
|
|
680
|
+
"data-sf-reader-input": true,
|
|
681
|
+
onChange: (e) => {
|
|
682
|
+
const f = e.target.files?.[0];
|
|
683
|
+
if (f) void read(f);
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
)
|
|
687
|
+
]
|
|
688
|
+
}
|
|
689
|
+
),
|
|
690
|
+
/* @__PURE__ */ jsx("p", { id: helpId, className: cx.privacy, "data-sf-reader-privacy": true, children: privacy }),
|
|
691
|
+
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,
|
|
692
|
+
/* @__PURE__ */ jsxs("p", { "data-sf-reader-links": true, children: [
|
|
693
|
+
spec.obtain_url ? /* @__PURE__ */ jsx("a", { href: spec.obtain_url, target: "_blank", rel: "noopener noreferrer", className: cx.link, "data-sf-reader-obtain": true, children: m.obtain }) : null,
|
|
694
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: cx.link, onClick: openManual, "data-sf-reader-manual": true, children: m.manual })
|
|
695
|
+
] })
|
|
696
|
+
] });
|
|
697
|
+
const reading = phase.kind === "reading" && /* @__PURE__ */ jsxs("div", { className: cx.root, "data-sf-reader": true, "data-state": "reading", children: [
|
|
698
|
+
/* @__PURE__ */ jsxs("p", { className: cx.status, "data-sf-reader-reading": true, children: [
|
|
699
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", "data-sf-reader-spinner": true }),
|
|
700
|
+
fill(m.reading, { document: documentName }),
|
|
701
|
+
" ",
|
|
702
|
+
/* @__PURE__ */ jsx("span", { "data-sf-reader-file": true, children: phase.fileName })
|
|
703
|
+
] }),
|
|
704
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: cx.secondary, onClick: cancel, "data-sf-reader-cancel": true, children: m.cancel })
|
|
705
|
+
] });
|
|
706
|
+
const errored = phase.kind === "error" && /* @__PURE__ */ jsxs("div", { className: cx.root, "data-sf-reader": true, "data-state": "error", children: [
|
|
707
|
+
/* @__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) }) }),
|
|
708
|
+
/* @__PURE__ */ jsxs("div", { className: cx.actions, "data-sf-reader-actions": true, children: [
|
|
709
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: cx.primary, onClick: openManual, "data-sf-reader-manual": true, children: m.manual }),
|
|
710
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: cx.secondary, onClick: () => setPhase({ kind: "idle" }), "data-sf-reader-retry": true, children: m.changeFile })
|
|
711
|
+
] })
|
|
712
|
+
] });
|
|
713
|
+
const review = phase.kind === "review" && /* @__PURE__ */ jsxs("div", { className: cx.root, "data-sf-reader": true, "data-state": "review", children: [
|
|
714
|
+
/* @__PURE__ */ jsx("h3", { ref: reviewRef, tabIndex: -1, "data-sf-reader-title": true, children: m.reviewTitle }),
|
|
715
|
+
/* @__PURE__ */ jsx("p", { "data-sf-reader-help": true, children: m.reviewHint }),
|
|
716
|
+
/* @__PURE__ */ jsx(
|
|
717
|
+
ErrorSummary,
|
|
718
|
+
{
|
|
719
|
+
items: summary,
|
|
720
|
+
focusKey: summaryKey,
|
|
721
|
+
locale,
|
|
722
|
+
title: summary ? summary.length === 1 ? m.summaryTitleOne : fill(m.summaryTitleMany, { n: summary.length }) : void 0
|
|
723
|
+
}
|
|
724
|
+
),
|
|
725
|
+
phase.extraction ? /* @__PURE__ */ jsx(Warnings, { x: phase.extraction, m, cx }) : null,
|
|
726
|
+
/* @__PURE__ */ jsx(
|
|
727
|
+
FiscalIdentityFields,
|
|
728
|
+
{
|
|
729
|
+
region,
|
|
730
|
+
values,
|
|
731
|
+
onChange: setValues,
|
|
732
|
+
errors: errorsByName,
|
|
733
|
+
idPrefix: `sf-reader-${reactId}`,
|
|
734
|
+
namePrefix,
|
|
735
|
+
fromDocument: phase.extraction?.from_document ?? [],
|
|
736
|
+
documentName,
|
|
737
|
+
regimeCandidates: phase.extraction && phase.extraction.regime_candidates.length > 1 ? phase.extraction.regime_candidates : void 0,
|
|
738
|
+
...personTypeOf ? { personTypeOf } : {},
|
|
739
|
+
locale,
|
|
740
|
+
...messages ? { messages } : {},
|
|
741
|
+
classNames: cx,
|
|
742
|
+
hideLegend: true
|
|
743
|
+
}
|
|
744
|
+
),
|
|
745
|
+
phase.extraction?.evidence ? /* @__PURE__ */ jsx("input", { type: "hidden", name: `${namePrefix}.document`, value: JSON.stringify(phase.extraction.evidence) }) : null,
|
|
746
|
+
canSave ? /* @__PURE__ */ jsxs("label", { "data-sf-reader-save": true, children: [
|
|
747
|
+
/* @__PURE__ */ jsx("input", { type: "checkbox", name: `${namePrefix}.save`, checked: save, onChange: (e) => setSave(e.target.checked) }),
|
|
748
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
749
|
+
m.saveToAccount,
|
|
750
|
+
/* @__PURE__ */ jsxs("span", { "data-sf-field-hint": true, children: [
|
|
751
|
+
" ",
|
|
752
|
+
m.saveConsent
|
|
753
|
+
] })
|
|
754
|
+
] })
|
|
755
|
+
] }) : null,
|
|
756
|
+
/* @__PURE__ */ jsxs("div", { className: cx.actions, "data-sf-reader-actions": true, children: [
|
|
757
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: cx.primary, onClick: confirm, "data-sf-reader-confirm": true, children: confirmLabel ?? m.confirm }),
|
|
758
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: cx.secondary, onClick: () => setPhase({ kind: "idle" }), "data-sf-reader-retry": true, children: m.changeFile })
|
|
759
|
+
] })
|
|
760
|
+
] });
|
|
761
|
+
return /* @__PURE__ */ jsxs("div", { "data-sf-document-reader": true, children: [
|
|
762
|
+
/* @__PURE__ */ jsx("div", { role: "status", "aria-live": "polite", className: cx.status, "data-sf-reader-live": true, style: visuallyHidden, children: live }),
|
|
763
|
+
phase.kind === "idle" ? idle : null,
|
|
764
|
+
reading || null,
|
|
765
|
+
errored || null,
|
|
766
|
+
review || null
|
|
767
|
+
] });
|
|
768
|
+
}
|
|
769
|
+
var visuallyHidden = { position: "absolute", width: 1, height: 1, margin: -1, padding: 0, overflow: "hidden", clip: "rect(0 0 0 0)", whiteSpace: "nowrap", border: 0 };
|
|
770
|
+
function Warnings({ x, m, cx }) {
|
|
771
|
+
const items = [];
|
|
772
|
+
if (x.qr_match === false) items.push(m.warnings.qrMismatch);
|
|
773
|
+
if (x.stale === true && x.age_days !== null) items.push(fill(m.warnings.stale, { days: x.age_days }));
|
|
774
|
+
if (x.regime_candidates.length === 0 && !x.values.tax_regime) items.push(m.warnings.regimeMissing);
|
|
775
|
+
else if (x.missing.some((k) => k !== "tax_regime" && k !== "document_id" && k !== "issued_on")) items.push(m.warnings.fieldsMissing);
|
|
776
|
+
if (!items.length) return null;
|
|
777
|
+
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: [
|
|
778
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "\u26A0 " }),
|
|
779
|
+
t
|
|
780
|
+
] }, t)) });
|
|
781
|
+
}
|
|
782
|
+
var profileValues = (p) => ({
|
|
783
|
+
tax_id: p.tax_id,
|
|
784
|
+
legal_name: p.legal_name,
|
|
785
|
+
postal_code: p.postal_code,
|
|
786
|
+
tax_regime: p.tax_regime,
|
|
787
|
+
use: p.invoice_use ?? ""
|
|
788
|
+
});
|
|
789
|
+
function InvoiceToggle(props) {
|
|
790
|
+
const { region, capabilities, profile, onChange, data, canSave, workerSrc, serverUpload, noJsHref, personTypeOf, namePrefix = "invoice", messages, classNames: cx = {}, now } = props;
|
|
791
|
+
const locale = useResolvedLocale(props.locale);
|
|
792
|
+
const all = fiscalMessages(locale, messages);
|
|
793
|
+
const m = all.toggle;
|
|
794
|
+
const fid = region.fiscal_id;
|
|
795
|
+
const spec = props.document ?? fid?.documents?.find((d) => !capabilities || capabilities.identity_documents.includes(d.kind)) ?? void 0;
|
|
796
|
+
const canRead = !!spec;
|
|
797
|
+
const usable = !!profile && !!profile.invoice_use;
|
|
798
|
+
const startMode = data?.extraction ? "read" : usable ? "profile" : profile ? "edit-profile" : canRead ? "read" : "manual";
|
|
799
|
+
const bodyId = `sf-invoice-${useId().replace(/:/g, "")}`;
|
|
800
|
+
const [requested, setRequested] = useState(!!props.defaultRequested || !!data?.extraction || !!data?.fieldErrors);
|
|
801
|
+
const [mode, setMode] = useState(startMode);
|
|
802
|
+
const [values, setValues] = useState(profile ? profileValues(profile) : { tax_id: "", legal_name: "", postal_code: "", tax_regime: "", use: "" });
|
|
803
|
+
const [confirmed, setConfirmed] = useState(null);
|
|
804
|
+
const [save, setSave] = useState(false);
|
|
805
|
+
const emit = useRef(onChange);
|
|
806
|
+
emit.current = onChange;
|
|
807
|
+
const payDefault = props.payLabel ?? m.payDefault;
|
|
808
|
+
const state = useMemo(() => {
|
|
809
|
+
if (!requested) return { requested: false, invoice: null, ready: false, source: null, payLabel: payDefault };
|
|
810
|
+
const country = region.country_code;
|
|
811
|
+
const label = m.payAndInvoice;
|
|
812
|
+
if (mode === "profile" && profile) {
|
|
813
|
+
const inv2 = toInvoiceRequest(profileValues(profile), profile.country_code || country);
|
|
814
|
+
if (profile.document) inv2.document = profile.document;
|
|
815
|
+
return { requested: true, invoice: inv2, ready: true, source: "profile", payLabel: label };
|
|
816
|
+
}
|
|
817
|
+
if (mode === "read") {
|
|
818
|
+
if (!confirmed) return { requested: true, invoice: null, ready: false, source: null, payLabel: label };
|
|
819
|
+
const inv2 = toInvoiceRequest(confirmed.values, country);
|
|
820
|
+
if (confirmed.evidence) inv2.document = confirmed.evidence;
|
|
821
|
+
if (confirmed.save) inv2.save = true;
|
|
822
|
+
return { requested: true, invoice: inv2, ready: true, source: confirmed.evidence ? "document" : "manual", payLabel: label };
|
|
823
|
+
}
|
|
824
|
+
const ready = Object.keys(validateFiscalValues(region, values, personTypeOf)).length === 0;
|
|
825
|
+
const inv = toInvoiceRequest(values, country);
|
|
826
|
+
if (save) inv.save = true;
|
|
827
|
+
return { requested: true, invoice: ready ? inv : null, ready, source: "manual", payLabel: label };
|
|
828
|
+
}, [requested, mode, profile, confirmed, values, save, region, personTypeOf, payDefault, m.payAndInvoice]);
|
|
829
|
+
const key = JSON.stringify(state);
|
|
830
|
+
useEffect(() => {
|
|
831
|
+
emit.current?.(state);
|
|
832
|
+
}, [key]);
|
|
833
|
+
if (capabilities && (!capabilities.available || capabilities.checkout === "off")) return null;
|
|
834
|
+
const profileCard = profile && /* @__PURE__ */ jsxs("div", { className: cx.card, "data-sf-invoice-profile": true, children: [
|
|
835
|
+
/* @__PURE__ */ jsx("p", { className: cx.cardTitle, "data-sf-invoice-profile-title": true, children: m.profileTitle }),
|
|
836
|
+
/* @__PURE__ */ jsxs("dl", { children: [
|
|
837
|
+
/* @__PURE__ */ jsx(Row, { cx, label: fid?.label || m.fieldsOf.taxId, value: /* @__PURE__ */ jsx("span", { "data-sf-masked": true, children: maskTaxId(profile.tax_id) }) }),
|
|
838
|
+
/* @__PURE__ */ jsx(Row, { cx, label: m.fieldsOf.legalName, value: profile.legal_name }),
|
|
839
|
+
/* @__PURE__ */ jsx(Row, { cx, label: m.fieldsOf.postalCode, value: profile.postal_code }),
|
|
840
|
+
/* @__PURE__ */ jsx(Row, { cx, label: m.fieldsOf.regime, value: labelOf(fid?.regimes, profile.tax_regime, locale) }),
|
|
841
|
+
/* @__PURE__ */ jsx(Row, { cx, label: m.fieldsOf.use, value: labelOf(fid?.invoice_uses, profile.invoice_use ?? "", locale) })
|
|
842
|
+
] }),
|
|
843
|
+
/* @__PURE__ */ jsx("input", { type: "hidden", name: `${namePrefix}.tax_id`, value: profile.tax_id }),
|
|
844
|
+
/* @__PURE__ */ jsx("input", { type: "hidden", name: `${namePrefix}.legal_name`, value: profile.legal_name }),
|
|
845
|
+
/* @__PURE__ */ jsx("input", { type: "hidden", name: `${namePrefix}.postal_code`, value: profile.postal_code }),
|
|
846
|
+
/* @__PURE__ */ jsx("input", { type: "hidden", name: `${namePrefix}.tax_regime`, value: profile.tax_regime }),
|
|
847
|
+
/* @__PURE__ */ jsx("input", { type: "hidden", name: `${namePrefix}.use`, value: profile.invoice_use ?? "" }),
|
|
848
|
+
profile.document ? /* @__PURE__ */ jsx("input", { type: "hidden", name: `${namePrefix}.document`, value: JSON.stringify(profile.document) }) : null,
|
|
849
|
+
/* @__PURE__ */ jsxs("div", { className: cx.actions, "data-sf-invoice-actions": true, children: [
|
|
850
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: cx.secondary, onClick: () => setMode("edit-profile"), "data-sf-invoice-change": true, children: m.change }),
|
|
851
|
+
/* @__PURE__ */ jsx(
|
|
852
|
+
"button",
|
|
853
|
+
{
|
|
854
|
+
type: "button",
|
|
855
|
+
className: cx.link,
|
|
856
|
+
onClick: () => {
|
|
857
|
+
setValues({ tax_id: "", legal_name: "", postal_code: "", tax_regime: "", use: "" });
|
|
858
|
+
setConfirmed(null);
|
|
859
|
+
setMode(canRead ? "read" : "manual");
|
|
860
|
+
},
|
|
861
|
+
"data-sf-invoice-other": true,
|
|
862
|
+
children: m.useOther
|
|
863
|
+
}
|
|
864
|
+
)
|
|
865
|
+
] })
|
|
866
|
+
] });
|
|
867
|
+
const saveBox = canSave ? /* @__PURE__ */ jsxs("label", { "data-sf-invoice-save": true, children: [
|
|
868
|
+
/* @__PURE__ */ jsx("input", { type: "checkbox", name: `${namePrefix}.save`, checked: save, onChange: (e) => setSave(e.target.checked) }),
|
|
869
|
+
/* @__PURE__ */ jsx("span", { children: all.reader.saveToAccount })
|
|
870
|
+
] }) : null;
|
|
871
|
+
const backToProfile = profile && usable && mode !== "profile" ? /* @__PURE__ */ jsx(
|
|
872
|
+
"button",
|
|
873
|
+
{
|
|
874
|
+
type: "button",
|
|
875
|
+
className: cx.link,
|
|
876
|
+
onClick: () => {
|
|
877
|
+
setValues(profileValues(profile));
|
|
878
|
+
setMode("profile");
|
|
879
|
+
},
|
|
880
|
+
"data-sf-invoice-back": true,
|
|
881
|
+
children: m.useSaved
|
|
882
|
+
}
|
|
883
|
+
) : null;
|
|
884
|
+
const body = !requested ? null : /* @__PURE__ */ jsxs("div", { id: bodyId, className: cx.body, "data-sf-invoice-body": true, children: [
|
|
885
|
+
/* @__PURE__ */ jsx("input", { type: "hidden", name: `${namePrefix}.requested`, value: "true" }),
|
|
886
|
+
mode === "profile" ? profileCard : mode === "read" ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
887
|
+
/* @__PURE__ */ jsx(
|
|
888
|
+
IdentityDocumentReader,
|
|
889
|
+
{
|
|
890
|
+
region,
|
|
891
|
+
...spec ? { document: spec } : {},
|
|
892
|
+
...workerSrc ? { workerSrc } : {},
|
|
893
|
+
...data ? { data } : {},
|
|
894
|
+
...canSave !== void 0 ? { canSave } : {},
|
|
895
|
+
...serverUpload ? { serverUpload } : {},
|
|
896
|
+
...props.documentName ? { documentName: props.documentName } : {},
|
|
897
|
+
...personTypeOf ? { personTypeOf } : {},
|
|
898
|
+
...now ? { now } : {},
|
|
899
|
+
onManual: () => {
|
|
900
|
+
setValues({ tax_id: "", legal_name: "", postal_code: "", tax_regime: "", use: "" });
|
|
901
|
+
setMode("manual");
|
|
902
|
+
},
|
|
903
|
+
onConfirm: (r) => {
|
|
904
|
+
setConfirmed(r);
|
|
905
|
+
},
|
|
906
|
+
namePrefix,
|
|
907
|
+
locale,
|
|
908
|
+
...messages ? { messages } : {},
|
|
909
|
+
classNames: cx
|
|
910
|
+
}
|
|
911
|
+
),
|
|
912
|
+
confirmed ? /* @__PURE__ */ jsxs("p", { role: "status", "data-sf-invoice-confirmed": true, children: [
|
|
913
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "\u2713 " }),
|
|
914
|
+
m.profileTitle
|
|
915
|
+
] }) : null,
|
|
916
|
+
backToProfile
|
|
917
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
918
|
+
/* @__PURE__ */ jsx("h3", { className: cx.cardTitle, children: m.manualTitle }),
|
|
919
|
+
/* @__PURE__ */ jsx(
|
|
920
|
+
FiscalIdentityFields,
|
|
921
|
+
{
|
|
922
|
+
region,
|
|
923
|
+
values,
|
|
924
|
+
onChange: setValues,
|
|
925
|
+
errors: Object.fromEntries(Object.entries(data?.fieldErrors ?? {}).map(([k, v]) => [k, all.server.errors[v] ?? v])),
|
|
926
|
+
namePrefix,
|
|
927
|
+
...personTypeOf ? { personTypeOf } : {},
|
|
928
|
+
locale,
|
|
929
|
+
...messages ? { messages } : {},
|
|
930
|
+
classNames: cx,
|
|
931
|
+
hideLegend: true
|
|
932
|
+
}
|
|
933
|
+
),
|
|
934
|
+
saveBox,
|
|
935
|
+
/* @__PURE__ */ jsx("p", { className: cx.privacy, "data-sf-invoice-privacy": true, children: m.privacy }),
|
|
936
|
+
backToProfile
|
|
937
|
+
] })
|
|
938
|
+
] });
|
|
939
|
+
return /* @__PURE__ */ jsxs("section", { className: cx.toggle, "data-sf-invoice-toggle": true, "aria-label": m.label, children: [
|
|
940
|
+
/* @__PURE__ */ jsxs("label", { "data-sf-invoice-label": true, children: [
|
|
941
|
+
/* @__PURE__ */ jsx(
|
|
942
|
+
"input",
|
|
943
|
+
{
|
|
944
|
+
type: "checkbox",
|
|
945
|
+
name: `${namePrefix}.want`,
|
|
946
|
+
checked: requested,
|
|
947
|
+
"aria-controls": requested ? bodyId : void 0,
|
|
948
|
+
onChange: (e) => setRequested(e.target.checked)
|
|
949
|
+
}
|
|
950
|
+
),
|
|
951
|
+
/* @__PURE__ */ jsx("span", { children: m.label })
|
|
952
|
+
] }),
|
|
953
|
+
noJsHref && !requested ? /* @__PURE__ */ jsx("noscript", { children: /* @__PURE__ */ jsx("a", { href: noJsHref, "data-sf-invoice-nojs": true, children: m.label }) }) : null,
|
|
954
|
+
body
|
|
955
|
+
] });
|
|
956
|
+
}
|
|
957
|
+
function Row({ label, value, cx }) {
|
|
958
|
+
return /* @__PURE__ */ jsxs("div", { className: cx.cardRow, "data-sf-invoice-row": true, children: [
|
|
959
|
+
/* @__PURE__ */ jsx("dt", { children: label }),
|
|
960
|
+
/* @__PURE__ */ jsx("dd", { children: value })
|
|
961
|
+
] });
|
|
962
|
+
}
|
|
963
|
+
function labelOf(list, code, locale) {
|
|
964
|
+
const e = list?.find((x) => x.code === code);
|
|
965
|
+
return e ? `${e.code} \xB7 ${entryLabel(e, locale)}` : code;
|
|
966
|
+
}
|
|
967
|
+
function invoiceCardState(invoice, available = true) {
|
|
968
|
+
if (!available) return "unavailable";
|
|
969
|
+
if (!invoice) return "not_requested";
|
|
970
|
+
const stage = invoice.stage ?? "";
|
|
971
|
+
const blocked = invoice.blocked_reason ?? "";
|
|
972
|
+
if (stage === "issued" || invoice.status === "issued" || invoice.action === "download" && (invoice.url || invoice.xml_url)) return "issued";
|
|
973
|
+
if (stage === "cancelled") return "cancelled";
|
|
974
|
+
if (stage === "needs_data" || invoice.action === "correct_data") return "needs_data";
|
|
975
|
+
if (stage === "needs_review" || invoice.action === "contact_store") return "needs_review";
|
|
976
|
+
if (stage === "window_closed" || blocked === "window_closed") return "window_closed";
|
|
977
|
+
if (stage === "awaiting_payment" || blocked === "awaiting_payment") return "awaiting_payment";
|
|
978
|
+
if (stage === "queued" || stage === "issuing" || stage === "retrying" || stage === "requested") return "processing";
|
|
979
|
+
if (blocked === "not_invoiceable" || blocked === "unavailable") return "unavailable";
|
|
980
|
+
if (invoice.can_request === false && blocked) return "unavailable";
|
|
981
|
+
return "not_requested";
|
|
982
|
+
}
|
|
983
|
+
function InvoiceStatusCard(props) {
|
|
984
|
+
const { invoice, available = true, onRequest, requestHref, onCorrect, correctHref, onRefresh, refreshMs = 1e4, refreshForMs = 6e4, contact, messages, classNames: cx = {}, headingLevel = 3 } = props;
|
|
985
|
+
const locale = useResolvedLocale(props.locale);
|
|
986
|
+
const m = fiscalMessages(locale, messages).status;
|
|
987
|
+
const state = invoiceCardState(invoice, available);
|
|
988
|
+
const titleId = `sf-invoice-card-${useId().replace(/:/g, "")}`;
|
|
989
|
+
const [tired, setTired] = useState(false);
|
|
990
|
+
const refresh = useRef(onRefresh);
|
|
991
|
+
refresh.current = onRefresh;
|
|
992
|
+
useEffect(() => {
|
|
993
|
+
setTired(false);
|
|
994
|
+
if (state !== "processing" || !refresh.current) return;
|
|
995
|
+
const t0 = Date.now();
|
|
996
|
+
const id = setInterval(() => {
|
|
997
|
+
if (Date.now() - t0 >= refreshForMs) {
|
|
998
|
+
clearInterval(id);
|
|
999
|
+
setTired(true);
|
|
1000
|
+
return;
|
|
1001
|
+
}
|
|
1002
|
+
refresh.current?.();
|
|
1003
|
+
}, refreshMs);
|
|
1004
|
+
return () => clearInterval(id);
|
|
1005
|
+
}, [state, refreshMs, refreshForMs]);
|
|
1006
|
+
const manualRefresh = useCallback(() => {
|
|
1007
|
+
setTired(false);
|
|
1008
|
+
refresh.current?.();
|
|
1009
|
+
}, []);
|
|
1010
|
+
const H = `h${headingLevel}`;
|
|
1011
|
+
const contactLine = contact?.email ?? contact?.phone ?? contact?.url;
|
|
1012
|
+
const reason = invoice?.reason_code ? m.reasons[invoice.reason_code] ?? m.reasons.default : m.reasons.default;
|
|
1013
|
+
let title;
|
|
1014
|
+
let body = null;
|
|
1015
|
+
let actions = null;
|
|
1016
|
+
let live = false;
|
|
1017
|
+
switch (state) {
|
|
1018
|
+
case "not_requested": {
|
|
1019
|
+
title = m.title;
|
|
1020
|
+
body = /* @__PURE__ */ jsx("p", { className: cx.body, children: m.requestHelp });
|
|
1021
|
+
const can = invoice?.can_request !== false;
|
|
1022
|
+
if (can && (onRequest || requestHref)) {
|
|
1023
|
+
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 });
|
|
1024
|
+
}
|
|
1025
|
+
break;
|
|
1026
|
+
}
|
|
1027
|
+
case "awaiting_payment":
|
|
1028
|
+
title = m.awaitingTitle;
|
|
1029
|
+
body = /* @__PURE__ */ jsx("p", { className: cx.body, children: m.awaitingBody });
|
|
1030
|
+
break;
|
|
1031
|
+
case "processing":
|
|
1032
|
+
live = true;
|
|
1033
|
+
title = m.processingTitle;
|
|
1034
|
+
body = /* @__PURE__ */ jsx("p", { className: cx.body, children: tired ? m.processingSlow : m.processingBody });
|
|
1035
|
+
actions = onRefresh ? /* @__PURE__ */ jsx("button", { type: "button", className: cx.secondary, onClick: manualRefresh, "data-sf-invoice-refresh": true, children: m.refresh }) : null;
|
|
1036
|
+
break;
|
|
1037
|
+
case "issued":
|
|
1038
|
+
title = m.issuedTitle;
|
|
1039
|
+
body = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1040
|
+
/* @__PURE__ */ jsx("p", { className: cx.body, children: m.issuedBody }),
|
|
1041
|
+
invoice?.folio ? /* @__PURE__ */ jsxs("p", { className: cx.meta, "data-sf-invoice-folio": true, children: [
|
|
1042
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
1043
|
+
m.folio,
|
|
1044
|
+
": "
|
|
1045
|
+
] }),
|
|
1046
|
+
/* @__PURE__ */ jsx("code", { children: invoice.folio })
|
|
1047
|
+
] }) : null
|
|
1048
|
+
] });
|
|
1049
|
+
actions = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1050
|
+
invoice?.url ? /* @__PURE__ */ jsx("a", { href: invoice.url, className: cx.primary, rel: "noopener noreferrer", download: true, "data-sf-invoice-pdf": true, children: m.pdf }) : null,
|
|
1051
|
+
invoice?.xml_url ? /* @__PURE__ */ jsx("a", { href: invoice.xml_url, className: cx.secondary, rel: "noopener noreferrer", download: true, "data-sf-invoice-xml": true, children: m.xml }) : null
|
|
1052
|
+
] });
|
|
1053
|
+
break;
|
|
1054
|
+
case "needs_data":
|
|
1055
|
+
title = m.needsDataTitle;
|
|
1056
|
+
body = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1057
|
+
/* @__PURE__ */ jsx("p", { className: cx.body, children: m.needsDataBody }),
|
|
1058
|
+
/* @__PURE__ */ jsx("p", { className: cx.meta, "data-sf-invoice-reason": true, children: reason })
|
|
1059
|
+
] });
|
|
1060
|
+
actions = onCorrect ? /* @__PURE__ */ jsx("button", { type: "button", className: cx.primary, onClick: onCorrect, "data-sf-invoice-correct": true, children: m.correct }) : correctHref ? /* @__PURE__ */ jsx("a", { href: correctHref, className: cx.primary, "data-sf-invoice-correct": true, children: m.correct }) : null;
|
|
1061
|
+
break;
|
|
1062
|
+
case "needs_review":
|
|
1063
|
+
title = m.reviewTitle;
|
|
1064
|
+
body = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1065
|
+
/* @__PURE__ */ jsx("p", { className: cx.body, children: m.reviewBody }),
|
|
1066
|
+
contactLine ? /* @__PURE__ */ jsx("p", { className: cx.meta, children: fill(m.contactBy, { contact: contactLine }) }) : null
|
|
1067
|
+
] });
|
|
1068
|
+
actions = contact?.url ? /* @__PURE__ */ jsx("a", { href: contact.url, className: cx.secondary, "data-sf-invoice-contact": true, children: m.contact }) : contact?.email ? /* @__PURE__ */ jsx("a", { href: `mailto:${contact.email}`, className: cx.secondary, "data-sf-invoice-contact": true, children: m.contact }) : null;
|
|
1069
|
+
break;
|
|
1070
|
+
case "window_closed":
|
|
1071
|
+
title = m.windowTitle;
|
|
1072
|
+
body = /* @__PURE__ */ jsx("p", { className: cx.body, children: m.windowBody });
|
|
1073
|
+
break;
|
|
1074
|
+
case "cancelled":
|
|
1075
|
+
title = m.cancelledTitle;
|
|
1076
|
+
body = /* @__PURE__ */ jsx("p", { className: cx.body, children: m.cancelledBody });
|
|
1077
|
+
break;
|
|
1078
|
+
default:
|
|
1079
|
+
title = m.unavailableTitle;
|
|
1080
|
+
body = /* @__PURE__ */ jsx("p", { className: cx.body, children: m.unavailableBody });
|
|
1081
|
+
}
|
|
1082
|
+
return /* @__PURE__ */ jsxs("section", { className: cx.root, "data-sf-invoice-card": true, "data-state": state, "data-stage": invoice?.stage || void 0, "aria-labelledby": titleId, children: [
|
|
1083
|
+
/* @__PURE__ */ jsxs(H, { id: titleId, className: cx.title, "data-sf-invoice-title": true, children: [
|
|
1084
|
+
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", "data-sf-invoice-icon": true, children: ICON[state] }),
|
|
1085
|
+
" ",
|
|
1086
|
+
title
|
|
1087
|
+
] }),
|
|
1088
|
+
/* @__PURE__ */ jsx("div", { "aria-live": live || state === "issued" ? "polite" : void 0, "data-sf-invoice-live": true, children: body }),
|
|
1089
|
+
actions ? /* @__PURE__ */ jsx("div", { className: cx.actions, "data-sf-invoice-actions": true, children: actions }) : null
|
|
1090
|
+
] });
|
|
1091
|
+
}
|
|
1092
|
+
var ICON = {
|
|
1093
|
+
not_requested: "\u25CB",
|
|
1094
|
+
awaiting_payment: "\u25D4",
|
|
1095
|
+
processing: "\u21BB",
|
|
1096
|
+
issued: "\u2713",
|
|
1097
|
+
needs_data: "!",
|
|
1098
|
+
needs_review: "\u25D0",
|
|
1099
|
+
window_closed: "\u2013",
|
|
1100
|
+
unavailable: "\u2013",
|
|
1101
|
+
cancelled: "\xD7"
|
|
1102
|
+
};
|
|
1103
|
+
|
|
1104
|
+
export { FiscalIdentityFields, IdentityDocumentReader, InvoiceStatusCard, InvoiceToggle, fiscalMessages, invoiceCardState };
|
|
1105
|
+
//# sourceMappingURL=fiscal.js.map
|
|
1106
|
+
//# sourceMappingURL=fiscal.js.map
|