@spaceinvoices/react-ui 0.4.8 → 0.4.11
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 +24 -8
- package/cli/dist/index.js +89 -26
- package/package.json +4 -1
- package/spaceinvoices.schema.json +6 -1
- package/src/common/autocomplete.tsx +69 -6
- package/src/components/advance-invoices/create/create-advance-invoice-form.tsx +124 -285
- package/src/components/advance-invoices/list/list-table.tsx +10 -3
- package/src/components/advance-invoices/list/locales/de.ts +2 -0
- package/src/components/advance-invoices/list/locales/en.ts +1 -0
- package/src/components/advance-invoices/list/locales/es.ts +1 -0
- package/src/components/advance-invoices/list/locales/fr.ts +1 -0
- package/src/components/advance-invoices/list/locales/hr.ts +1 -0
- package/src/components/advance-invoices/list/locales/it.ts +1 -0
- package/src/components/advance-invoices/list/locales/nl.ts +1 -0
- package/src/components/advance-invoices/list/locales/pl.ts +1 -0
- package/src/components/advance-invoices/list/locales/pt.ts +1 -0
- package/src/components/advance-invoices/list/locales/sl.ts +1 -0
- package/src/components/advance-invoices/list/use-advance-invoice-download.ts +1 -12
- package/src/components/credit-notes/create/create-credit-note-form.tsx +116 -238
- package/src/components/credit-notes/list/list-table.tsx +6 -3
- package/src/components/credit-notes/list/use-credit-note-download.ts +1 -12
- package/src/components/customers/customer-autocomplete.tsx +64 -11
- package/src/components/customers/customer-list-table/customer-list-table.tsx +3 -2
- package/src/components/dashboard/collection-rate-card/collection-rate-card.tsx +9 -1
- package/src/components/dashboard/collection-rate-card/locales/bg.ts +3 -0
- package/src/components/dashboard/collection-rate-card/locales/cs.ts +3 -0
- package/src/components/dashboard/collection-rate-card/locales/et.ts +3 -0
- package/src/components/dashboard/collection-rate-card/locales/fi.ts +3 -0
- package/src/components/dashboard/collection-rate-card/locales/is.ts +3 -0
- package/src/components/dashboard/collection-rate-card/locales/nb.ts +3 -0
- package/src/components/dashboard/collection-rate-card/locales/sk.ts +3 -0
- package/src/components/dashboard/collection-rate-card/locales/sv.ts +3 -0
- package/src/components/dashboard/invoice-status-chart/invoice-status-chart.tsx +10 -2
- package/src/components/dashboard/invoice-status-chart/locales/bg.ts +10 -0
- package/src/components/dashboard/invoice-status-chart/locales/cs.ts +10 -0
- package/src/components/dashboard/invoice-status-chart/locales/de.ts +1 -0
- package/src/components/dashboard/invoice-status-chart/locales/es.ts +1 -0
- package/src/components/dashboard/invoice-status-chart/locales/et.ts +10 -0
- package/src/components/dashboard/invoice-status-chart/locales/fi.ts +10 -0
- package/src/components/dashboard/invoice-status-chart/locales/fr.ts +1 -0
- package/src/components/dashboard/invoice-status-chart/locales/hr.ts +1 -0
- package/src/components/dashboard/invoice-status-chart/locales/is.ts +10 -0
- package/src/components/dashboard/invoice-status-chart/locales/it.ts +1 -0
- package/src/components/dashboard/invoice-status-chart/locales/nb.ts +10 -0
- package/src/components/dashboard/invoice-status-chart/locales/nl.ts +1 -0
- package/src/components/dashboard/invoice-status-chart/locales/pl.ts +1 -0
- package/src/components/dashboard/invoice-status-chart/locales/pt.ts +1 -0
- package/src/components/dashboard/invoice-status-chart/locales/sk.ts +10 -0
- package/src/components/dashboard/invoice-status-chart/locales/sl.ts +1 -0
- package/src/components/dashboard/invoice-status-chart/locales/sv.ts +10 -0
- package/src/components/dashboard/payment-methods-chart/locales/bg.ts +12 -0
- package/src/components/dashboard/payment-methods-chart/locales/cs.ts +12 -0
- package/src/components/dashboard/payment-methods-chart/locales/et.ts +12 -0
- package/src/components/dashboard/payment-methods-chart/locales/fi.ts +12 -0
- package/src/components/dashboard/payment-methods-chart/locales/is.ts +12 -0
- package/src/components/dashboard/payment-methods-chart/locales/nb.ts +12 -0
- package/src/components/dashboard/payment-methods-chart/locales/sk.ts +12 -0
- package/src/components/dashboard/payment-methods-chart/locales/sv.ts +12 -0
- package/src/components/dashboard/payment-methods-chart/payment-methods-chart.tsx +9 -1
- package/src/components/dashboard/payment-trend-chart/locales/bg.ts +6 -0
- package/src/components/dashboard/payment-trend-chart/locales/cs.ts +6 -0
- package/src/components/dashboard/payment-trend-chart/locales/de.ts +1 -0
- package/src/components/dashboard/payment-trend-chart/locales/es.ts +1 -0
- package/src/components/dashboard/payment-trend-chart/locales/et.ts +6 -0
- package/src/components/dashboard/payment-trend-chart/locales/fi.ts +6 -0
- package/src/components/dashboard/payment-trend-chart/locales/fr.ts +1 -0
- package/src/components/dashboard/payment-trend-chart/locales/hr.ts +1 -0
- package/src/components/dashboard/payment-trend-chart/locales/is.ts +6 -0
- package/src/components/dashboard/payment-trend-chart/locales/it.ts +1 -0
- package/src/components/dashboard/payment-trend-chart/locales/nb.ts +6 -0
- package/src/components/dashboard/payment-trend-chart/locales/nl.ts +1 -0
- package/src/components/dashboard/payment-trend-chart/locales/pl.ts +1 -0
- package/src/components/dashboard/payment-trend-chart/locales/pt.ts +1 -0
- package/src/components/dashboard/payment-trend-chart/locales/sk.ts +6 -0
- package/src/components/dashboard/payment-trend-chart/locales/sl.ts +1 -0
- package/src/components/dashboard/payment-trend-chart/locales/sv.ts +6 -0
- package/src/components/dashboard/payment-trend-chart/payment-trend-chart.tsx +15 -8
- package/src/components/dashboard/revenue-trend-chart/locales/bg.ts +6 -0
- package/src/components/dashboard/revenue-trend-chart/locales/cs.ts +6 -0
- package/src/components/dashboard/revenue-trend-chart/locales/de.ts +1 -0
- package/src/components/dashboard/revenue-trend-chart/locales/es.ts +1 -0
- package/src/components/dashboard/revenue-trend-chart/locales/et.ts +6 -0
- package/src/components/dashboard/revenue-trend-chart/locales/fi.ts +6 -0
- package/src/components/dashboard/revenue-trend-chart/locales/fr.ts +1 -0
- package/src/components/dashboard/revenue-trend-chart/locales/hr.ts +1 -0
- package/src/components/dashboard/revenue-trend-chart/locales/is.ts +6 -0
- package/src/components/dashboard/revenue-trend-chart/locales/it.ts +1 -0
- package/src/components/dashboard/revenue-trend-chart/locales/nb.ts +6 -0
- package/src/components/dashboard/revenue-trend-chart/locales/nl.ts +1 -0
- package/src/components/dashboard/revenue-trend-chart/locales/pl.ts +1 -0
- package/src/components/dashboard/revenue-trend-chart/locales/pt.ts +1 -0
- package/src/components/dashboard/revenue-trend-chart/locales/sk.ts +6 -0
- package/src/components/dashboard/revenue-trend-chart/locales/sl.ts +1 -0
- package/src/components/dashboard/revenue-trend-chart/locales/sv.ts +6 -0
- package/src/components/dashboard/revenue-trend-chart/revenue-trend-chart.tsx +15 -8
- package/src/components/dashboard/tax-collected-card/locales.ts +110 -0
- package/src/components/dashboard/tax-collected-card/tax-collected-card.tsx +8 -2
- package/src/components/dashboard/tax-collected-card/use-tax-collected.ts +4 -4
- package/src/components/dashboard/top-customers-chart/locales/bg.ts +7 -0
- package/src/components/dashboard/top-customers-chart/locales/cs.ts +7 -0
- package/src/components/dashboard/top-customers-chart/locales/de.ts +2 -0
- package/src/components/dashboard/top-customers-chart/locales/es.ts +2 -0
- package/src/components/dashboard/top-customers-chart/locales/et.ts +7 -0
- package/src/components/dashboard/top-customers-chart/locales/fi.ts +7 -0
- package/src/components/dashboard/top-customers-chart/locales/fr.ts +2 -0
- package/src/components/dashboard/top-customers-chart/locales/hr.ts +2 -0
- package/src/components/dashboard/top-customers-chart/locales/is.ts +7 -0
- package/src/components/dashboard/top-customers-chart/locales/it.ts +2 -0
- package/src/components/dashboard/top-customers-chart/locales/nb.ts +7 -0
- package/src/components/dashboard/top-customers-chart/locales/nl.ts +2 -0
- package/src/components/dashboard/top-customers-chart/locales/pl.ts +2 -0
- package/src/components/dashboard/top-customers-chart/locales/pt.ts +2 -0
- package/src/components/dashboard/top-customers-chart/locales/sk.ts +7 -0
- package/src/components/dashboard/top-customers-chart/locales/sl.ts +2 -0
- package/src/components/dashboard/top-customers-chart/locales/sv.ts +7 -0
- package/src/components/dashboard/top-customers-chart/top-customers-chart.tsx +23 -12
- package/src/components/delivery-notes/create/create-delivery-note-form.tsx +33 -20
- package/src/components/delivery-notes/list/list-table.tsx +22 -13
- package/src/components/delivery-notes/list/locales/de.ts +2 -0
- package/src/components/delivery-notes/list/locales/en.ts +1 -0
- package/src/components/delivery-notes/list/locales/es.ts +1 -0
- package/src/components/delivery-notes/list/locales/fr.ts +1 -0
- package/src/components/delivery-notes/list/locales/hr.ts +1 -0
- package/src/components/delivery-notes/list/locales/it.ts +1 -0
- package/src/components/delivery-notes/list/locales/nl.ts +1 -0
- package/src/components/delivery-notes/list/locales/pl.ts +1 -0
- package/src/components/delivery-notes/list/locales/pt.ts +1 -0
- package/src/components/delivery-notes/list/locales/sl.ts +1 -0
- package/src/components/delivery-notes/list/use-delivery-note-download.ts +1 -12
- package/src/components/documents/create/document-add-item-form.tsx +28 -16
- package/src/components/documents/create/document-add-item-tax-rate-field.tsx +12 -2
- package/src/components/documents/create/document-items-section.tsx +70 -39
- package/src/components/documents/create/document-recipient-section.tsx +10 -1
- package/src/components/documents/create/live-preview.tsx +113 -15
- package/src/components/documents/create/prepare-document-submission.ts +35 -16
- package/src/components/documents/create/use-document-customer-form.ts +14 -3
- package/src/components/documents/documents.hooks.ts +7 -2
- package/src/components/documents/shared/document-preview-display.tsx +136 -67
- package/src/components/documents/shared/scaled-document-preview.tsx +45 -5
- package/src/components/documents/view/document-actions-bar.tsx +284 -182
- package/src/components/documents/view/document-activities-list.tsx +3 -0
- package/src/components/documents/view/document-payments-list.tsx +3 -0
- package/src/components/documents/view/locales/de.ts +8 -0
- package/src/components/documents/view/locales/es.ts +8 -0
- package/src/components/documents/view/locales/fr.ts +8 -0
- package/src/components/documents/view/locales/hr.ts +8 -0
- package/src/components/documents/view/locales/it.ts +8 -0
- package/src/components/documents/view/locales/nl.ts +8 -0
- package/src/components/documents/view/locales/pl.ts +8 -0
- package/src/components/documents/view/locales/pt.ts +8 -0
- package/src/components/documents/view/locales/sl.ts +8 -0
- package/src/components/documents/view/use-document-download.ts +14 -25
- package/src/components/entities/create-entity-form.tsx +101 -16
- package/src/components/entities/entity-settings-form/entity-settings-form.tsx +10 -10
- package/src/components/entities/entity-settings-form/locales/de.ts +10 -0
- package/src/components/entities/entity-settings-form/locales/es.ts +10 -0
- package/src/components/entities/entity-settings-form/locales/fr.ts +10 -0
- package/src/components/entities/entity-settings-form/locales/hr.ts +10 -0
- package/src/components/entities/entity-settings-form/locales/it.ts +10 -0
- package/src/components/entities/entity-settings-form/locales/nl.ts +10 -0
- package/src/components/entities/entity-settings-form/locales/pl.ts +10 -0
- package/src/components/entities/entity-settings-form/locales/pt.ts +10 -0
- package/src/components/entities/entity-settings-form/locales/sl.ts +10 -0
- package/src/components/entities/fina-settings-form/fina-operator-required-dialog.tsx +3 -3
- package/src/components/entities/fina-settings-form/fina-settings-form.tsx +78 -124
- package/src/components/entities/fina-settings-form/sections/certificate-settings-section.tsx +8 -1
- package/src/components/entities/fina-settings-form/sections/premises-management-section.tsx +14 -2
- package/src/components/entities/fina-settings-form/sections/register-premise-dialog.tsx +7 -2
- package/src/components/entities/furs-settings-form/furs-settings-form.tsx +56 -130
- package/src/components/entities/furs-settings-form/sections/certificate-settings-section.tsx +8 -1
- package/src/components/entities/furs-settings-form/sections/enable-fiscalization-section.tsx +1 -0
- package/src/components/entities/furs-settings-form/sections/general-settings-section.tsx +15 -2
- package/src/components/entities/furs-settings-form/sections/premises-management-section.tsx +20 -3
- package/src/components/entities/furs-settings-form/sections/register-premise-dialog.tsx +38 -12
- package/src/components/entities/settings/defaults-settings-form.tsx +6 -6
- package/src/components/entities/settings/eslog-settings-form.tsx +13 -1
- package/src/components/entities/settings/pdf-template-selector/demo-invoice-data.ts +3 -22
- package/src/components/entities/shared/fiscalization-step-flow.ts +77 -0
- package/src/components/entities/shared/fiscalization-step-tabs.tsx +71 -0
- package/src/components/estimates/create/create-estimate-form.tsx +34 -21
- package/src/components/estimates/list/list-table.tsx +23 -14
- package/src/components/estimates/list/locales/de.ts +2 -0
- package/src/components/estimates/list/locales/en.ts +1 -0
- package/src/components/estimates/list/locales/es.ts +1 -0
- package/src/components/estimates/list/locales/fr.ts +1 -0
- package/src/components/estimates/list/locales/hr.ts +1 -0
- package/src/components/estimates/list/locales/it.ts +1 -0
- package/src/components/estimates/list/locales/nl.ts +1 -0
- package/src/components/estimates/list/locales/pl.ts +1 -0
- package/src/components/estimates/list/locales/pt.ts +1 -0
- package/src/components/estimates/list/locales/sl.ts +1 -0
- package/src/components/estimates/list/use-estimate-download.ts +1 -12
- package/src/components/export/document-export-form.tsx +33 -7
- package/src/components/export/sales-per-item-export-form.tsx +23 -7
- package/src/components/invoices/create/create-invoice-form.tsx +295 -329
- package/src/components/invoices/create/prepare-invoice-submission.ts +0 -8
- package/src/components/invoices/list/list-table.tsx +7 -4
- package/src/components/invoices/list/use-invoice-download.ts +1 -11
- package/src/components/invoices/send-email-dialog/locales/de.ts +20 -0
- package/src/components/invoices/send-email-dialog/locales/es.ts +20 -0
- package/src/components/invoices/send-email-dialog/locales/fr.ts +20 -0
- package/src/components/invoices/send-email-dialog/locales/hr.ts +20 -0
- package/src/components/invoices/send-email-dialog/locales/it.ts +20 -0
- package/src/components/invoices/send-email-dialog/locales/nl.ts +20 -0
- package/src/components/invoices/send-email-dialog/locales/pl.ts +20 -0
- package/src/components/invoices/send-email-dialog/locales/pt.ts +20 -0
- package/src/components/invoices/send-email-dialog/locales/sl.ts +20 -0
- package/src/components/invoices/send-email-dialog/send-email-dialog.tsx +77 -8
- package/src/components/invoices/view/eslog-info-display.tsx +17 -1
- package/src/components/invoices/view/fiscalization-status-card.tsx +7 -3
- package/src/components/items/item-combobox.tsx +26 -6
- package/src/components/items/item-list-table/item-list-table.tsx +5 -2
- package/src/components/payments/create-payment-form/index.ts +1 -0
- package/src/components/payments/list/list-table.tsx +14 -4
- package/src/components/recurring-invoices/list/list-table.tsx +7 -4
- package/src/components/request-logs/locales.ts +412 -0
- package/src/components/request-logs/request-log-detail.tsx +37 -21
- package/src/components/request-logs/request-log-list-table.tsx +57 -11
- package/src/components/table/data-table.tsx +5 -2
- package/src/components/table/date-cell.tsx +3 -1
- package/src/components/table/filter-bar.tsx +14 -2
- package/src/components/table/hooks/use-table-query.ts +1 -1
- package/src/components/table/locales.ts +1116 -0
- package/src/components/table/search-input.tsx +12 -3
- package/src/components/table/selection-toolbar.tsx +23 -6
- package/src/components/table/table-empty-state.tsx +43 -3
- package/src/components/table/table-no-results.tsx +3 -3
- package/src/components/table/table-pagination.tsx +4 -3
- package/src/components/table/types.ts +1 -0
- package/src/components/tax-reports/index.ts +1 -0
- package/src/components/tax-reports/kir-export-form.tsx +46 -8
- package/src/components/tax-reports/slovenia-tax-profile-step.tsx +191 -0
- package/src/components/tax-reports/slovenia-yearly-export-form.tsx +509 -0
- package/src/components/tax-reports/slovenia-yearly-review-step.tsx +253 -0
- package/src/components/tax-reports/slovenia-yearly-summary.tsx +19 -0
- package/src/components/taxes/tax-list-table/tax-list-table.tsx +3 -2
- package/src/components/ui/sidebar.tsx +3 -2
- package/src/components/ui/sticky-form-footer.tsx +7 -1
- package/src/components/webhook-logs/index.ts +6 -0
- package/src/components/webhook-logs/locales.ts +392 -0
- package/src/components/webhook-logs/webhook-delivery-detail.tsx +255 -0
- package/src/components/webhook-logs/webhook-delivery-list-table.tsx +278 -0
- package/src/components/wl-subscription/index.ts +1 -0
- package/src/components/wl-subscription/locked-feature.tsx +1 -0
- package/src/components/wl-subscription/paywall.tsx +193 -0
- package/src/components/wl-subscription/upgrade-modal.tsx +93 -29
- package/src/generate-schemas.ts +12 -7
- package/src/generated/schemas/customer.ts +2 -0
- package/src/generated/schemas/entity.ts +134 -0
- package/src/generated/schemas/exportsloveniayearlynormiranireport_body.ts +27 -0
- package/src/generated/schemas/index.ts +2 -0
- package/src/generated/schemas/me.ts +20 -1
- package/src/generated/schemas/renderadvanceinvoicepreview_body.ts +40 -34
- package/src/generated/schemas/rendercreditnotepreview_body.ts +42 -36
- package/src/generated/schemas/renderdeliverynotepreview_body.ts +23 -13
- package/src/generated/schemas/renderestimatepreview_body.ts +23 -13
- package/src/generated/schemas/renderinvoicepreview_body.ts +40 -34
- package/src/generated/schemas/sendemail_body.ts +44 -0
- package/src/generated/schemas/sloveniataxprofile.ts +42 -0
- package/src/generated/schemas/startpdfexport_body.ts +91 -1
- package/src/generated/schemas/webhook.ts +10 -0
- package/src/hooks/use-duplicate-document.ts +51 -13
- package/src/hooks/use-eslog-validation.ts +59 -0
- package/src/hooks/use-premise-selection.ts +186 -0
- package/src/lib/browser-cookies.ts +4 -4
- package/src/lib/date-fns-locale.ts +48 -0
- package/src/lib/fiscalization-options.ts +81 -0
- package/src/lib/locale.ts +38 -0
- package/src/lib/template-variables.tsx +1 -1
- package/src/lib/translation.ts +14 -3
- package/src/providers/entities-context.tsx +1 -0
- package/src/providers/entities-provider.tsx +102 -3
- package/src/providers/form-footer-context.tsx +37 -4
- package/src/providers/sdk-provider.tsx +7 -2
- package/src/providers/white-label-provider.tsx +4 -1
- package/src/providers/wl-subscription-provider.tsx +90 -3
|
@@ -15,6 +15,13 @@ import { useQuery } from "@tanstack/react-query";
|
|
|
15
15
|
import { useEntities } from "@/ui/providers/entities-context";
|
|
16
16
|
import { useSDK } from "@/ui/providers/sdk-provider";
|
|
17
17
|
|
|
18
|
+
const DUPLICATE_TIMING_EVENT = "si:duplicate-timing";
|
|
19
|
+
|
|
20
|
+
function emitDuplicateDebug(detail: Record<string, unknown>) {
|
|
21
|
+
if (!import.meta.env.DEV || typeof window === "undefined") return;
|
|
22
|
+
window.dispatchEvent(new CustomEvent(DUPLICATE_TIMING_EVENT, { detail }));
|
|
23
|
+
}
|
|
24
|
+
|
|
18
25
|
export type DocumentType = "invoice" | "estimate" | "credit_note" | "advance_invoice" | "delivery_note";
|
|
19
26
|
type Document = Invoice | Estimate | CreditNote | AdvanceInvoice | DeliveryNote;
|
|
20
27
|
type CreateRequest =
|
|
@@ -127,9 +134,21 @@ function transformDocumentForDuplication(source: Document, targetType: DocumentT
|
|
|
127
134
|
// Number - leave empty for auto-generation
|
|
128
135
|
// Do NOT copy: number, totals, taxes, payments, furs, eslog, vies, shareable_id
|
|
129
136
|
// Link back to source document when converting (e.g., delivery note → invoice)
|
|
130
|
-
|
|
137
|
+
// Skip linking if source is a draft (drafts have no number/fiscalization)
|
|
138
|
+
...(isConversion && !(source as any).is_draft ? { linked_documents: [source.id] } : {}),
|
|
131
139
|
};
|
|
132
140
|
|
|
141
|
+
// Copy service dates when source is an invoice (available on invoices and credit notes)
|
|
142
|
+
if (sourceType === "invoice" || sourceType === "credit_note") {
|
|
143
|
+
const sourceDoc = source as any;
|
|
144
|
+
if (sourceDoc.date_service) {
|
|
145
|
+
(baseData as any).date_service = sourceDoc.date_service;
|
|
146
|
+
}
|
|
147
|
+
if (sourceDoc.date_service_to) {
|
|
148
|
+
(baseData as any).date_service_to = sourceDoc.date_service_to;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
133
152
|
return baseData;
|
|
134
153
|
}
|
|
135
154
|
|
|
@@ -192,6 +211,13 @@ export function useDuplicateDocument({
|
|
|
192
211
|
throw new Error("Source document ID and entity ID are required");
|
|
193
212
|
}
|
|
194
213
|
|
|
214
|
+
const startedAt = performance.now();
|
|
215
|
+
emitDuplicateDebug({
|
|
216
|
+
stage: "request_started",
|
|
217
|
+
sourceId,
|
|
218
|
+
sourceType,
|
|
219
|
+
targetType,
|
|
220
|
+
});
|
|
195
221
|
// Fetch source document based on its type
|
|
196
222
|
let source: Document;
|
|
197
223
|
if (sourceType === "invoice") {
|
|
@@ -215,22 +241,34 @@ export function useDuplicateDocument({
|
|
|
215
241
|
|
|
216
242
|
// Build source document summaries for conversions (different source → target type)
|
|
217
243
|
const isConversion = sourceType !== targetType;
|
|
218
|
-
const sourceDocuments: LinkedDocumentSummary[] =
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
244
|
+
const sourceDocuments: LinkedDocumentSummary[] =
|
|
245
|
+
isConversion && !(source as any).is_draft
|
|
246
|
+
? [
|
|
247
|
+
{
|
|
248
|
+
id: source.id,
|
|
249
|
+
type: sourceType,
|
|
250
|
+
number: (source as any).number || "",
|
|
251
|
+
date: (source as any).date || "",
|
|
252
|
+
total_with_tax: (source as any).total_with_tax ?? 0,
|
|
253
|
+
currency_code: (source as any).currency_code || "",
|
|
254
|
+
},
|
|
255
|
+
]
|
|
256
|
+
: [];
|
|
257
|
+
|
|
258
|
+
emitDuplicateDebug({
|
|
259
|
+
stage: "request_succeeded",
|
|
260
|
+
sourceId,
|
|
261
|
+
sourceType,
|
|
262
|
+
targetType,
|
|
263
|
+
elapsedMs: Number((performance.now() - startedAt).toFixed(1)),
|
|
264
|
+
});
|
|
230
265
|
|
|
231
266
|
return { initialValues, sourceDocuments };
|
|
232
267
|
},
|
|
233
268
|
enabled: enabled && !!sourceId && !!activeEntity?.id && !!sourceType,
|
|
269
|
+
retry: false,
|
|
270
|
+
refetchOnWindowFocus: false,
|
|
271
|
+
refetchOnReconnect: false,
|
|
234
272
|
});
|
|
235
273
|
|
|
236
274
|
return {
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared hook for eSLOG validation state.
|
|
3
|
+
*
|
|
4
|
+
* Encapsulates the eSLOG availability check, enabled toggle, and
|
|
5
|
+
* entity error tracking duplicated across invoice and advance invoice forms.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { useEffect, useState } from "react";
|
|
9
|
+
|
|
10
|
+
type EslogError = { field: string; message: string };
|
|
11
|
+
|
|
12
|
+
type Entity = {
|
|
13
|
+
country_code?: string;
|
|
14
|
+
settings?: Record<string, any>;
|
|
15
|
+
} | null;
|
|
16
|
+
|
|
17
|
+
export type EslogValidationResult = {
|
|
18
|
+
/** Whether eSLOG is available (SI entity + setting enabled) */
|
|
19
|
+
isAvailable: boolean;
|
|
20
|
+
/** Current enabled state (undefined until initialized) */
|
|
21
|
+
isEnabled: boolean | undefined;
|
|
22
|
+
/** Toggle eSLOG validation on/off */
|
|
23
|
+
setEnabled: (v: boolean) => void;
|
|
24
|
+
/** Entity-level errors that require settings updates */
|
|
25
|
+
entityErrors: EslogError[];
|
|
26
|
+
/** Update entity errors (e.g. from validation results) */
|
|
27
|
+
setEntityErrors: (errors: EslogError[]) => void;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export function useEslogValidation(activeEntity: Entity): EslogValidationResult {
|
|
31
|
+
const isSlovenianEntity = activeEntity?.country_code === "SI";
|
|
32
|
+
const entityEslogEnabled = !!(activeEntity?.settings as any)?.eslog_validation_enabled;
|
|
33
|
+
const isAvailable = isSlovenianEntity && entityEslogEnabled;
|
|
34
|
+
|
|
35
|
+
const [isEnabled, setEnabled] = useState<boolean | undefined>(undefined);
|
|
36
|
+
const [entityErrors, setEntityErrors] = useState<EslogError[]>([]);
|
|
37
|
+
|
|
38
|
+
// Auto-enable when available and not yet initialized
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
if (isAvailable && isEnabled === undefined) {
|
|
41
|
+
setEnabled(true);
|
|
42
|
+
}
|
|
43
|
+
}, [isAvailable, isEnabled]);
|
|
44
|
+
|
|
45
|
+
// Clear entity errors when disabled
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
if (!isEnabled) {
|
|
48
|
+
setEntityErrors([]);
|
|
49
|
+
}
|
|
50
|
+
}, [isEnabled]);
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
isAvailable,
|
|
54
|
+
isEnabled,
|
|
55
|
+
setEnabled,
|
|
56
|
+
entityErrors,
|
|
57
|
+
setEntityErrors,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared hook for FURS/FINA premise and device selection.
|
|
3
|
+
*
|
|
4
|
+
* Encapsulates the premise/device loading, localStorage persistence,
|
|
5
|
+
* auto-selection, and readiness logic duplicated across invoice,
|
|
6
|
+
* credit note, and advance invoice create forms.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { useEffect, useMemo, useState } from "react";
|
|
10
|
+
|
|
11
|
+
import { useFinaPremises, useFinaSettings } from "../components/entities/fina-settings-form/fina-settings.hooks";
|
|
12
|
+
import { useFursPremises, useFursSettings } from "../components/entities/furs-settings-form/furs-settings.hooks";
|
|
13
|
+
import {
|
|
14
|
+
type FinaCombo,
|
|
15
|
+
type FursCombo,
|
|
16
|
+
getLastUsedFinaCombo,
|
|
17
|
+
getLastUsedFursCombo,
|
|
18
|
+
setLastUsedFinaCombo,
|
|
19
|
+
setLastUsedFursCombo,
|
|
20
|
+
} from "../components/invoices/invoices.hooks";
|
|
21
|
+
|
|
22
|
+
/** Minimal premise shape needed by the hook */
|
|
23
|
+
type Premise = {
|
|
24
|
+
id: string;
|
|
25
|
+
business_premise_name: string;
|
|
26
|
+
is_active?: boolean;
|
|
27
|
+
Devices?: Array<{
|
|
28
|
+
electronic_device_name: string;
|
|
29
|
+
is_active?: boolean;
|
|
30
|
+
}>;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export type PremiseSelectionResult = {
|
|
34
|
+
/** Whether the fiscalization system is enabled in settings */
|
|
35
|
+
isEnabled: boolean;
|
|
36
|
+
/** Raw settings object (for form-specific logic like unified_numbering) */
|
|
37
|
+
settings: any;
|
|
38
|
+
/** Whether settings/premises are still loading */
|
|
39
|
+
isLoading: boolean;
|
|
40
|
+
/** Active (non-archived) premises */
|
|
41
|
+
activePremises: Premise[];
|
|
42
|
+
/** Whether there are any active premises */
|
|
43
|
+
hasPremises: boolean;
|
|
44
|
+
/** Active devices for the currently selected premise */
|
|
45
|
+
activeDevices: Array<{ electronic_device_name: string; is_active?: boolean }>;
|
|
46
|
+
/** Currently selected premise name */
|
|
47
|
+
selectedPremiseName: string | undefined;
|
|
48
|
+
/** Currently selected device name */
|
|
49
|
+
selectedDeviceName: string | undefined;
|
|
50
|
+
/** Update selected premise */
|
|
51
|
+
setSelectedPremiseName: (name: string | undefined) => void;
|
|
52
|
+
/** Update selected device */
|
|
53
|
+
setSelectedDeviceName: (name: string | undefined) => void;
|
|
54
|
+
/** True when disabled OR both premise+device are selected */
|
|
55
|
+
isSelectionReady: boolean;
|
|
56
|
+
/** True when enabled, has premises, and both are selected */
|
|
57
|
+
isActive: boolean;
|
|
58
|
+
/** Save current combo to localStorage (call on successful submission) */
|
|
59
|
+
saveCombo: () => void;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export function usePremiseSelection(opts: {
|
|
63
|
+
entityId: string;
|
|
64
|
+
type: "furs" | "fina";
|
|
65
|
+
enabled?: boolean;
|
|
66
|
+
}): PremiseSelectionResult {
|
|
67
|
+
const { entityId, type } = opts;
|
|
68
|
+
const externalEnabled = opts.enabled !== false;
|
|
69
|
+
|
|
70
|
+
// --- FURS hooks (only called when type is furs) ---
|
|
71
|
+
const { data: fursSettings, isLoading: isFursSettingsLoading } = useFursSettings(entityId, {
|
|
72
|
+
enabled: type === "furs" && externalEnabled,
|
|
73
|
+
});
|
|
74
|
+
const { data: fursPremises, isLoading: isFursPremisesLoading } = useFursPremises(entityId, {
|
|
75
|
+
enabled: type === "furs" && externalEnabled && fursSettings?.enabled === true,
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// --- FINA hooks (only called when type is fina) ---
|
|
79
|
+
const { data: finaSettings, isLoading: isFinaSettingsLoading } = useFinaSettings(entityId, {
|
|
80
|
+
enabled: type === "fina" && externalEnabled,
|
|
81
|
+
});
|
|
82
|
+
const { data: finaPremises, isLoading: isFinaPremisesLoading } = useFinaPremises(entityId, {
|
|
83
|
+
enabled: type === "fina" && externalEnabled && finaSettings?.enabled === true,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// Unified values
|
|
87
|
+
const settings = type === "furs" ? fursSettings : finaSettings;
|
|
88
|
+
const premises = type === "furs" ? fursPremises : finaPremises;
|
|
89
|
+
const isSettingsLoading = type === "furs" ? isFursSettingsLoading : isFinaSettingsLoading;
|
|
90
|
+
const isPremisesLoading = type === "furs" ? isFursPremisesLoading : isFinaPremisesLoading;
|
|
91
|
+
|
|
92
|
+
const isEnabled = settings?.enabled === true;
|
|
93
|
+
const isLoading = isSettingsLoading || (isEnabled && isPremisesLoading);
|
|
94
|
+
|
|
95
|
+
const activePremises = useMemo(() => (premises?.filter((p: any) => p.is_active) as Premise[]) || [], [premises]);
|
|
96
|
+
const hasPremises = activePremises.length > 0;
|
|
97
|
+
|
|
98
|
+
// Selection state
|
|
99
|
+
const [selectedPremiseName, setSelectedPremiseName] = useState<string | undefined>();
|
|
100
|
+
const [selectedDeviceName, setSelectedDeviceName] = useState<string | undefined>();
|
|
101
|
+
|
|
102
|
+
// Active devices for selected premise
|
|
103
|
+
const activeDevices = useMemo(() => {
|
|
104
|
+
if (!selectedPremiseName) return [];
|
|
105
|
+
const premise = activePremises.find((p) => p.business_premise_name === selectedPremiseName);
|
|
106
|
+
const devices = premise?.Devices?.filter((d) => d.is_active) || [];
|
|
107
|
+
// For FURS, exclude the legacy "OLD" device
|
|
108
|
+
return type === "furs" ? devices.filter((d) => d.electronic_device_name !== "OLD") : devices;
|
|
109
|
+
}, [activePremises, selectedPremiseName, type]);
|
|
110
|
+
|
|
111
|
+
// Initialize selection from localStorage or first active combo
|
|
112
|
+
useEffect(() => {
|
|
113
|
+
if (!isEnabled || !hasPremises || selectedPremiseName) return;
|
|
114
|
+
|
|
115
|
+
const lastUsed = type === "furs" ? getLastUsedFursCombo(entityId) : getLastUsedFinaCombo(entityId);
|
|
116
|
+
|
|
117
|
+
if (lastUsed) {
|
|
118
|
+
const premise = activePremises.find((p) => p.business_premise_name === lastUsed.business_premise_name);
|
|
119
|
+
const device = premise?.Devices?.find(
|
|
120
|
+
(d) => d.electronic_device_name === lastUsed.electronic_device_name && d.is_active,
|
|
121
|
+
);
|
|
122
|
+
if (premise && device) {
|
|
123
|
+
setSelectedPremiseName(lastUsed.business_premise_name);
|
|
124
|
+
setSelectedDeviceName(lastUsed.electronic_device_name);
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Fall back to first active premise/device
|
|
130
|
+
const firstPremise = activePremises[0];
|
|
131
|
+
const firstDevice = firstPremise?.Devices?.find((d) => d.is_active);
|
|
132
|
+
if (firstPremise && firstDevice) {
|
|
133
|
+
setSelectedPremiseName(firstPremise.business_premise_name);
|
|
134
|
+
setSelectedDeviceName(firstDevice.electronic_device_name);
|
|
135
|
+
}
|
|
136
|
+
}, [isEnabled, hasPremises, activePremises, entityId, selectedPremiseName, type]);
|
|
137
|
+
|
|
138
|
+
// When premise changes, auto-select first active device if current is invalid
|
|
139
|
+
useEffect(() => {
|
|
140
|
+
if (!selectedPremiseName) return;
|
|
141
|
+
const premise = activePremises.find((p) => p.business_premise_name === selectedPremiseName);
|
|
142
|
+
const devicesForPremise =
|
|
143
|
+
type === "furs"
|
|
144
|
+
? premise?.Devices?.filter((d) => d.is_active && d.electronic_device_name !== "OLD")
|
|
145
|
+
: premise?.Devices?.filter((d) => d.is_active);
|
|
146
|
+
const firstDevice = devicesForPremise?.[0];
|
|
147
|
+
if (firstDevice && selectedDeviceName !== firstDevice.electronic_device_name) {
|
|
148
|
+
const currentDeviceInPremise = devicesForPremise?.find((d) => d.electronic_device_name === selectedDeviceName);
|
|
149
|
+
if (!currentDeviceInPremise) {
|
|
150
|
+
setSelectedDeviceName(firstDevice.electronic_device_name);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}, [selectedPremiseName, activePremises, selectedDeviceName, type]);
|
|
154
|
+
|
|
155
|
+
const isSelectionReady = !isEnabled || !hasPremises || (!!selectedPremiseName && !!selectedDeviceName);
|
|
156
|
+
const isActive = !!(isEnabled && hasPremises && selectedPremiseName && selectedDeviceName);
|
|
157
|
+
|
|
158
|
+
const saveCombo = () => {
|
|
159
|
+
if (!isActive || !selectedPremiseName || !selectedDeviceName) return;
|
|
160
|
+
const combo: FursCombo | FinaCombo = {
|
|
161
|
+
business_premise_name: selectedPremiseName,
|
|
162
|
+
electronic_device_name: selectedDeviceName,
|
|
163
|
+
};
|
|
164
|
+
if (type === "furs") {
|
|
165
|
+
setLastUsedFursCombo(entityId, combo);
|
|
166
|
+
} else {
|
|
167
|
+
setLastUsedFinaCombo(entityId, combo);
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
return {
|
|
172
|
+
isEnabled,
|
|
173
|
+
settings,
|
|
174
|
+
isLoading: !!isLoading,
|
|
175
|
+
activePremises,
|
|
176
|
+
hasPremises,
|
|
177
|
+
activeDevices,
|
|
178
|
+
selectedPremiseName,
|
|
179
|
+
selectedDeviceName,
|
|
180
|
+
setSelectedPremiseName,
|
|
181
|
+
setSelectedDeviceName,
|
|
182
|
+
isSelectionReady,
|
|
183
|
+
isActive,
|
|
184
|
+
saveCombo,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
@@ -16,7 +16,7 @@ type CookieOptions = {
|
|
|
16
16
|
* Sets a cookie with the given name, value, and options
|
|
17
17
|
*/
|
|
18
18
|
export function setCookie(name: string, value: string, options: CookieOptions = {}) {
|
|
19
|
-
if (typeof
|
|
19
|
+
if (typeof document === "undefined") {
|
|
20
20
|
console.warn("setCookie called on server");
|
|
21
21
|
return;
|
|
22
22
|
}
|
|
@@ -41,7 +41,7 @@ export function setCookie(name: string, value: string, options: CookieOptions =
|
|
|
41
41
|
* Gets a cookie value by name
|
|
42
42
|
*/
|
|
43
43
|
export function getCookie(name: string): string | undefined {
|
|
44
|
-
if (typeof
|
|
44
|
+
if (typeof document === "undefined") {
|
|
45
45
|
return undefined;
|
|
46
46
|
}
|
|
47
47
|
|
|
@@ -52,7 +52,7 @@ export function getCookie(name: string): string | undefined {
|
|
|
52
52
|
* Removes a cookie by name, clearing both host-only and domain-scoped versions
|
|
53
53
|
*/
|
|
54
54
|
export function deleteCookie(name: string, path = "/") {
|
|
55
|
-
if (typeof
|
|
55
|
+
if (typeof document === "undefined") {
|
|
56
56
|
console.warn("deleteCookie called on server");
|
|
57
57
|
return;
|
|
58
58
|
}
|
|
@@ -64,7 +64,7 @@ export function deleteCookie(name: string, path = "/") {
|
|
|
64
64
|
});
|
|
65
65
|
|
|
66
66
|
// Also delete domain cookie (e.g. .spaceinvoices.com) if on spaceinvoices.com
|
|
67
|
-
const hostname = window.location.hostname;
|
|
67
|
+
const hostname = typeof window !== "undefined" ? window.location.hostname : globalThis.location?.hostname ?? "";
|
|
68
68
|
if (hostname === "spaceinvoices.com" || hostname.endsWith(".spaceinvoices.com")) {
|
|
69
69
|
setCookie(name, "", {
|
|
70
70
|
path,
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { Locale } from "date-fns";
|
|
2
|
+
import {
|
|
3
|
+
bg,
|
|
4
|
+
cs,
|
|
5
|
+
de,
|
|
6
|
+
enUS,
|
|
7
|
+
es,
|
|
8
|
+
et,
|
|
9
|
+
fi,
|
|
10
|
+
fr,
|
|
11
|
+
hr,
|
|
12
|
+
is,
|
|
13
|
+
it,
|
|
14
|
+
nb,
|
|
15
|
+
nl,
|
|
16
|
+
pl,
|
|
17
|
+
pt,
|
|
18
|
+
sk,
|
|
19
|
+
sl,
|
|
20
|
+
sv,
|
|
21
|
+
} from "date-fns/locale";
|
|
22
|
+
import { getLocaleLanguage } from "./locale";
|
|
23
|
+
|
|
24
|
+
const DATE_FNS_LOCALES: Record<string, Locale> = {
|
|
25
|
+
bg,
|
|
26
|
+
cs,
|
|
27
|
+
de,
|
|
28
|
+
en: enUS,
|
|
29
|
+
es,
|
|
30
|
+
et,
|
|
31
|
+
fi,
|
|
32
|
+
fr,
|
|
33
|
+
hr,
|
|
34
|
+
is,
|
|
35
|
+
it,
|
|
36
|
+
nb,
|
|
37
|
+
nl,
|
|
38
|
+
pl,
|
|
39
|
+
pt,
|
|
40
|
+
sk,
|
|
41
|
+
sl,
|
|
42
|
+
sv,
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export function getDateFnsLocale(locale?: string): Locale {
|
|
46
|
+
const language = getLocaleLanguage(locale);
|
|
47
|
+
return DATE_FNS_LOCALES[language ?? ""] ?? enUS;
|
|
48
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure functions for building FURS/FINA/eSLOG submission options.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from the submit callbacks of invoice, credit note,
|
|
5
|
+
* and advance invoice forms where this logic was duplicated.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export type FursSubmitOptions = { skip: true } | { business_premise_name: string; electronic_device_name: string };
|
|
9
|
+
|
|
10
|
+
export type FinaSubmitOptions = {
|
|
11
|
+
business_premise_name: string;
|
|
12
|
+
electronic_device_name: string;
|
|
13
|
+
payment_type: string;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export type EslogSubmitOptions = {
|
|
17
|
+
validation_enabled: boolean;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Build FURS fiscalization options for document submission.
|
|
22
|
+
*
|
|
23
|
+
* Returns undefined when FURS should not be included (drafts, disabled, edit mode).
|
|
24
|
+
*/
|
|
25
|
+
export function buildFursOptions(opts: {
|
|
26
|
+
isDraft: boolean;
|
|
27
|
+
isEnabled: boolean;
|
|
28
|
+
isEditMode?: boolean;
|
|
29
|
+
skipFiscalization?: boolean;
|
|
30
|
+
premiseName?: string;
|
|
31
|
+
deviceName?: string;
|
|
32
|
+
}): FursSubmitOptions | undefined {
|
|
33
|
+
if (opts.isDraft || opts.isEditMode || !opts.isEnabled) return undefined;
|
|
34
|
+
if (opts.skipFiscalization) return { skip: true };
|
|
35
|
+
if (opts.premiseName && opts.deviceName) {
|
|
36
|
+
return {
|
|
37
|
+
business_premise_name: opts.premiseName,
|
|
38
|
+
electronic_device_name: opts.deviceName,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Build FINA fiscalization options for document submission.
|
|
46
|
+
*
|
|
47
|
+
* Returns undefined when FINA should not be included (drafts, numbering disabled).
|
|
48
|
+
*/
|
|
49
|
+
export function buildFinaOptions(opts: {
|
|
50
|
+
isDraft: boolean;
|
|
51
|
+
useFinaNumbering: boolean;
|
|
52
|
+
isEditMode?: boolean;
|
|
53
|
+
premiseName?: string;
|
|
54
|
+
deviceName?: string;
|
|
55
|
+
paymentType?: string;
|
|
56
|
+
}): FinaSubmitOptions | undefined {
|
|
57
|
+
if (opts.isDraft || opts.isEditMode || !opts.useFinaNumbering) return undefined;
|
|
58
|
+
if (opts.premiseName && opts.deviceName) {
|
|
59
|
+
return {
|
|
60
|
+
business_premise_name: opts.premiseName,
|
|
61
|
+
electronic_device_name: opts.deviceName,
|
|
62
|
+
payment_type: opts.paymentType || "bank_transfer",
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Build eSLOG validation options for document submission.
|
|
70
|
+
*
|
|
71
|
+
* Returns undefined when eSLOG should not be included (drafts, edit mode, unavailable).
|
|
72
|
+
*/
|
|
73
|
+
export function buildEslogOptions(opts: {
|
|
74
|
+
isDraft: boolean;
|
|
75
|
+
isEditMode?: boolean;
|
|
76
|
+
isAvailable: boolean;
|
|
77
|
+
isEnabled: boolean | undefined;
|
|
78
|
+
}): EslogSubmitOptions | undefined {
|
|
79
|
+
if (opts.isDraft || opts.isEditMode || !opts.isAvailable) return undefined;
|
|
80
|
+
return { validation_enabled: opts.isEnabled === true };
|
|
81
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export function normalizeLocale(locale?: string, fallback = "en"): string {
|
|
2
|
+
return locale?.trim().toLowerCase().replace(/_/g, "-") || fallback;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export function getLocaleLanguage(locale?: string, fallback = "en"): string {
|
|
6
|
+
return normalizeLocale(locale, fallback).split("-")[0] || fallback;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const LANGUAGE_TO_LOCALE: Record<string, string> = {
|
|
10
|
+
en: "en-US",
|
|
11
|
+
de: "de-DE",
|
|
12
|
+
sl: "sl-SI",
|
|
13
|
+
it: "it-IT",
|
|
14
|
+
fr: "fr-FR",
|
|
15
|
+
es: "es-ES",
|
|
16
|
+
pt: "pt-PT",
|
|
17
|
+
nl: "nl-NL",
|
|
18
|
+
pl: "pl-PL",
|
|
19
|
+
hr: "hr-HR",
|
|
20
|
+
sv: "sv-SE",
|
|
21
|
+
fi: "fi-FI",
|
|
22
|
+
et: "et-EE",
|
|
23
|
+
bg: "bg-BG",
|
|
24
|
+
cs: "cs-CZ",
|
|
25
|
+
sk: "sk-SK",
|
|
26
|
+
nb: "nb-NO",
|
|
27
|
+
is: "is-IS",
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export function getFullLocale(locale?: string, fallback = "en-US"): string {
|
|
31
|
+
const normalized = normalizeLocale(locale, fallback);
|
|
32
|
+
if (Object.values(LANGUAGE_TO_LOCALE).includes(normalized)) {
|
|
33
|
+
return normalized;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const language = getLocaleLanguage(normalized, fallback.split("-")[0]);
|
|
37
|
+
return LANGUAGE_TO_LOCALE[language] || fallback;
|
|
38
|
+
}
|
|
@@ -26,7 +26,7 @@ export function getVariableValue(
|
|
|
26
26
|
|
|
27
27
|
// Entity-related variables
|
|
28
28
|
if (varName === "entity_name") return entity.name || null;
|
|
29
|
-
if (varName === "entity_email") return
|
|
29
|
+
if (varName === "entity_email") return entity.email || null;
|
|
30
30
|
if (varName === "entity_address") return entity.address || null;
|
|
31
31
|
if (varName === "entity_post_code") return entity.post_code || null;
|
|
32
32
|
if (varName === "entity_city") return entity.city || null;
|
package/src/lib/translation.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useCallback } from "react";
|
|
2
|
+
import { getLocaleLanguage, normalizeLocale } from "./locale";
|
|
2
3
|
|
|
3
4
|
type TranslationFunction = (key: string) => string;
|
|
4
5
|
|
|
@@ -24,9 +25,19 @@ export function createTranslation({ t, namespace, locale = "en", translations =
|
|
|
24
25
|
}
|
|
25
26
|
}
|
|
26
27
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
const normalizedLocale = normalizeLocale(locale);
|
|
29
|
+
const baseLocale = getLocaleLanguage(normalizedLocale);
|
|
30
|
+
|
|
31
|
+
// 2. Look up in local translations for current locale, then base language
|
|
32
|
+
const localeTranslations = translations[normalizedLocale] ?? translations[baseLocale];
|
|
33
|
+
if (localeTranslations) {
|
|
34
|
+
const translation = localeTranslations[key];
|
|
35
|
+
if (translation) return translation;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const englishTranslations = translations.en;
|
|
39
|
+
if (englishTranslations) {
|
|
40
|
+
const translation = englishTranslations[key];
|
|
30
41
|
if (translation) return translation;
|
|
31
42
|
}
|
|
32
43
|
|
|
@@ -12,6 +12,7 @@ export type EntitiesContextType = {
|
|
|
12
12
|
activeEntity: Entity | null;
|
|
13
13
|
setActiveEntity: (entity: Entity | null) => void;
|
|
14
14
|
environment: EntityEnvironment;
|
|
15
|
+
/** @deprecated Use URL-based sandbox routing instead of calling setEnvironment directly */
|
|
15
16
|
setEnvironment: (environment: EntityEnvironment) => void;
|
|
16
17
|
isLoading: boolean;
|
|
17
18
|
refetchEntities: () => Promise<void>;
|