@spaceinvoices/react-ui 0.4.7 → 0.4.10
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/cli/dist/index.js +1 -1
- package/package.json +1 -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/create/locales/de.ts +2 -1
- package/src/components/advance-invoices/create/locales/es.ts +2 -1
- package/src/components/advance-invoices/create/locales/fr.ts +2 -1
- package/src/components/advance-invoices/create/locales/hr.ts +2 -1
- package/src/components/advance-invoices/create/locales/it.ts +2 -1
- package/src/components/advance-invoices/create/locales/nl.ts +2 -1
- package/src/components/advance-invoices/create/locales/pl.ts +2 -1
- package/src/components/advance-invoices/create/locales/pt.ts +2 -1
- package/src/components/advance-invoices/create/locales/sl.ts +2 -1
- 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/create/locales/de.ts +2 -1
- package/src/components/credit-notes/create/locales/es.ts +2 -1
- package/src/components/credit-notes/create/locales/fr.ts +2 -1
- package/src/components/credit-notes/create/locales/hr.ts +2 -1
- package/src/components/credit-notes/create/locales/it.ts +2 -1
- package/src/components/credit-notes/create/locales/nl.ts +2 -1
- package/src/components/credit-notes/create/locales/pl.ts +2 -1
- package/src/components/credit-notes/create/locales/pt.ts +2 -1
- package/src/components/credit-notes/create/locales/sl.ts +2 -1
- 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/create/locales/de.ts +2 -1
- package/src/components/delivery-notes/create/locales/es.ts +2 -1
- package/src/components/delivery-notes/create/locales/fr.ts +2 -1
- package/src/components/delivery-notes/create/locales/hr.ts +2 -1
- package/src/components/delivery-notes/create/locales/it.ts +2 -1
- package/src/components/delivery-notes/create/locales/nl.ts +2 -1
- package/src/components/delivery-notes/create/locales/pl.ts +2 -1
- package/src/components/delivery-notes/create/locales/pt.ts +2 -1
- package/src/components/delivery-notes/create/locales/sl.ts +2 -1
- 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/locales/de.ts +6 -3
- package/src/components/entities/entity-settings-form/locales/es.ts +6 -3
- package/src/components/entities/entity-settings-form/locales/fr.ts +6 -3
- package/src/components/entities/entity-settings-form/locales/hr.ts +4 -2
- package/src/components/entities/entity-settings-form/locales/it.ts +6 -3
- package/src/components/entities/entity-settings-form/locales/nl.ts +6 -3
- package/src/components/entities/entity-settings-form/locales/pl.ts +6 -2
- package/src/components/entities/entity-settings-form/locales/pt.ts +6 -3
- package/src/components/entities/entity-settings-form/locales/sl.ts +4 -2
- 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/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/create/locales/de.ts +2 -1
- package/src/components/estimates/create/locales/es.ts +2 -1
- package/src/components/estimates/create/locales/fr.ts +2 -1
- package/src/components/estimates/create/locales/hr.ts +2 -1
- package/src/components/estimates/create/locales/it.ts +2 -1
- package/src/components/estimates/create/locales/nl.ts +2 -1
- package/src/components/estimates/create/locales/pl.ts +2 -1
- package/src/components/estimates/create/locales/pt.ts +2 -1
- package/src/components/estimates/create/locales/sl.ts +2 -1
- 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/locales/de.ts +2 -1
- package/src/components/invoices/create/locales/es.ts +2 -1
- package/src/components/invoices/create/locales/fr.ts +2 -1
- package/src/components/invoices/create/locales/hr.ts +2 -1
- package/src/components/invoices/create/locales/it.ts +2 -1
- package/src/components/invoices/create/locales/nl.ts +2 -1
- package/src/components/invoices/create/locales/pl.ts +2 -1
- package/src/components/invoices/create/locales/pt.ts +2 -1
- package/src/components/invoices/create/locales/sl.ts +2 -1
- 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 +2 -0
- package/src/components/invoices/send-email-dialog/locales/es.ts +2 -0
- package/src/components/invoices/send-email-dialog/locales/fr.ts +2 -0
- package/src/components/invoices/send-email-dialog/locales/hr.ts +2 -0
- package/src/components/invoices/send-email-dialog/locales/it.ts +2 -0
- package/src/components/invoices/send-email-dialog/locales/nl.ts +2 -0
- package/src/components/invoices/send-email-dialog/locales/pl.ts +2 -0
- package/src/components/invoices/send-email-dialog/locales/pt.ts +2 -0
- package/src/components/invoices/send-email-dialog/locales/sl.ts +2 -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/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/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 +10 -5
- package/src/generated/schemas/customer.ts +2 -0
- package/src/generated/schemas/entity.ts +34 -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/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
|
@@ -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>;
|
|
@@ -26,6 +26,10 @@ type EntitiesProviderProps = {
|
|
|
26
26
|
cookieDomain?: string;
|
|
27
27
|
/** When provided (from URL param), this entity ID is used as the source of truth instead of the cookie */
|
|
28
28
|
urlEntityId?: string;
|
|
29
|
+
/** When provided, determines environment from URL instead of cookie. true = sandbox, false = live. */
|
|
30
|
+
isSandbox?: boolean;
|
|
31
|
+
/** Called when urlEntityId is not found in any environment, allowing parent to try other accounts */
|
|
32
|
+
onEntityNotFound?: (entityId: string) => boolean | void | Promise<boolean | void>;
|
|
29
33
|
};
|
|
30
34
|
|
|
31
35
|
export function EntitiesProvider({
|
|
@@ -34,6 +38,8 @@ export function EntitiesProvider({
|
|
|
34
38
|
onNoEntities,
|
|
35
39
|
cookieDomain,
|
|
36
40
|
urlEntityId,
|
|
41
|
+
isSandbox,
|
|
42
|
+
onEntityNotFound,
|
|
37
43
|
}: EntitiesProviderProps) {
|
|
38
44
|
const { sdk, isInitialized } = useSDK();
|
|
39
45
|
const [cookies, setCookie, removeCookie] = useCookies([ACTIVE_ENTITY_COOKIE, ACTIVE_ENVIRONMENT_COOKIE]);
|
|
@@ -44,12 +50,24 @@ export function EntitiesProvider({
|
|
|
44
50
|
// URL entity ID takes precedence over cookie
|
|
45
51
|
const resolvedEntityId = urlEntityId ?? initialEntityIdFromCookie;
|
|
46
52
|
|
|
53
|
+
// When isSandbox is provided (URL-driven), use it as source of truth; otherwise fall back to cookie
|
|
47
54
|
const resolvedInitialEnvironment: EntityEnvironment =
|
|
48
|
-
|
|
55
|
+
isSandbox !== undefined
|
|
56
|
+
? isSandbox
|
|
57
|
+
? "sandbox"
|
|
58
|
+
: "live"
|
|
59
|
+
: (initialEnvironmentFromCookie ?? (initialActiveEntity?.environment as EntityEnvironment | undefined) ?? "live");
|
|
49
60
|
|
|
50
61
|
const [environment, setEnvironmentState] = useState<EntityEnvironment>(resolvedInitialEnvironment);
|
|
51
62
|
const previousEnvironment = useRef(environment);
|
|
52
63
|
|
|
64
|
+
// Sync environment with isSandbox prop when it changes (URL-driven navigation)
|
|
65
|
+
useEffect(() => {
|
|
66
|
+
if (isSandbox === undefined) return;
|
|
67
|
+
const target: EntityEnvironment = isSandbox ? "sandbox" : "live";
|
|
68
|
+
setEnvironmentState((current) => (current === target ? current : target));
|
|
69
|
+
}, [isSandbox]);
|
|
70
|
+
|
|
53
71
|
// Store the initial entity ID (from URL or cookie) so we can match it when entities load
|
|
54
72
|
const initialEntityIdRef = useRef(resolvedEntityId);
|
|
55
73
|
|
|
@@ -65,6 +83,7 @@ export function EntitiesProvider({
|
|
|
65
83
|
const {
|
|
66
84
|
data: entities = [],
|
|
67
85
|
isLoading,
|
|
86
|
+
isFetching,
|
|
68
87
|
refetch,
|
|
69
88
|
isError,
|
|
70
89
|
error,
|
|
@@ -88,10 +107,50 @@ export function EntitiesProvider({
|
|
|
88
107
|
});
|
|
89
108
|
|
|
90
109
|
// When no entities in current environment, check the other before giving up
|
|
110
|
+
// Skip auto-switch when isSandbox is explicitly set (URL-driven) — respect the user's intent
|
|
91
111
|
const hasCalledNoEntities = useRef(false);
|
|
92
112
|
const hasTriedFallback = useRef(false);
|
|
113
|
+
const resolvingUrlEntityRef = useRef<string | null>(null);
|
|
114
|
+
const emptyUrlEntityRefetchKeyRef = useRef<string | null>(null);
|
|
93
115
|
useEffect(() => {
|
|
94
|
-
if (isLoading || entities.length > 0 || hasCalledNoEntities.current) return;
|
|
116
|
+
if (isLoading || isFetching || entities.length > 0 || hasCalledNoEntities.current) return;
|
|
117
|
+
|
|
118
|
+
if (urlEntityId) {
|
|
119
|
+
const refetchKey = `${urlEntityId}:${environment}`;
|
|
120
|
+
if (emptyUrlEntityRefetchKeyRef.current !== refetchKey) {
|
|
121
|
+
emptyUrlEntityRefetchKeyRef.current = refetchKey;
|
|
122
|
+
void refetch();
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (resolvingUrlEntityRef.current === urlEntityId) return;
|
|
127
|
+
resolvingUrlEntityRef.current = urlEntityId;
|
|
128
|
+
|
|
129
|
+
if (!onEntityNotFound) {
|
|
130
|
+
hasCalledNoEntities.current = true;
|
|
131
|
+
onNoEntities?.();
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
void Promise.resolve(onEntityNotFound(urlEntityId))
|
|
136
|
+
.then((resolved) => {
|
|
137
|
+
if (resolved) return;
|
|
138
|
+
hasCalledNoEntities.current = true;
|
|
139
|
+
onNoEntities?.();
|
|
140
|
+
})
|
|
141
|
+
.catch(() => {
|
|
142
|
+
hasCalledNoEntities.current = true;
|
|
143
|
+
onNoEntities?.();
|
|
144
|
+
});
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// When environment is explicitly set via URL, don't auto-switch to the other environment
|
|
149
|
+
if (isSandbox !== undefined) {
|
|
150
|
+
hasCalledNoEntities.current = true;
|
|
151
|
+
onNoEntities?.();
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
95
154
|
|
|
96
155
|
// Try the other environment before calling onNoEntities
|
|
97
156
|
if (!hasTriedFallback.current && sdk) {
|
|
@@ -118,7 +177,18 @@ export function EntitiesProvider({
|
|
|
118
177
|
if (!hasTriedFallback.current) return;
|
|
119
178
|
hasCalledNoEntities.current = true;
|
|
120
179
|
onNoEntities?.();
|
|
121
|
-
}, [
|
|
180
|
+
}, [
|
|
181
|
+
isLoading,
|
|
182
|
+
isFetching,
|
|
183
|
+
entities.length,
|
|
184
|
+
onEntityNotFound,
|
|
185
|
+
onNoEntities,
|
|
186
|
+
refetch,
|
|
187
|
+
sdk,
|
|
188
|
+
environment,
|
|
189
|
+
isSandbox,
|
|
190
|
+
urlEntityId,
|
|
191
|
+
]);
|
|
122
192
|
|
|
123
193
|
// Memoize entities to prevent unnecessary re-renders
|
|
124
194
|
const memoizedEntities = useMemo(() => entities, [entities]);
|
|
@@ -130,6 +200,13 @@ export function EntitiesProvider({
|
|
|
130
200
|
}
|
|
131
201
|
}, [environment]);
|
|
132
202
|
|
|
203
|
+
useEffect(() => {
|
|
204
|
+
if (entities.length > 0) {
|
|
205
|
+
emptyUrlEntityRefetchKeyRef.current = null;
|
|
206
|
+
resolvingUrlEntityRef.current = null;
|
|
207
|
+
}
|
|
208
|
+
}, [entities.length]);
|
|
209
|
+
|
|
133
210
|
// Sync active entity when entities list changes
|
|
134
211
|
// Use ref to read current activeEntityState without causing re-runs
|
|
135
212
|
const activeEntityRef = useRef(activeEntityState);
|
|
@@ -139,6 +216,8 @@ export function EntitiesProvider({
|
|
|
139
216
|
useEffect(() => {
|
|
140
217
|
if (urlEntityId) {
|
|
141
218
|
initialEntityIdRef.current = urlEntityId;
|
|
219
|
+
resolvingUrlEntityRef.current = null;
|
|
220
|
+
emptyUrlEntityRefetchKeyRef.current = null;
|
|
142
221
|
}
|
|
143
222
|
}, [urlEntityId]);
|
|
144
223
|
|
|
@@ -201,6 +280,26 @@ export function EntitiesProvider({
|
|
|
201
280
|
setEnvironmentState(altEnv);
|
|
202
281
|
}, [urlEntityId, memoizedEntities, isLoading, environment]);
|
|
203
282
|
|
|
283
|
+
// After environment fallback has been attempted and entity is still not found, notify parent
|
|
284
|
+
const entityNotFoundCallbackFired = useRef<string | null>(null);
|
|
285
|
+
useEffect(() => {
|
|
286
|
+
if (!urlEntityId || !onEntityNotFound || isLoading) return;
|
|
287
|
+
if (memoizedEntities.length === 0) return;
|
|
288
|
+
|
|
289
|
+
const found = memoizedEntities.some((e) => e.id === urlEntityId);
|
|
290
|
+
if (found) {
|
|
291
|
+
entityNotFoundCallbackFired.current = null;
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// Only fire after the environment fallback has already been attempted for this entity
|
|
296
|
+
if (urlEntityFallbackAttempted.current !== urlEntityId) return;
|
|
297
|
+
if (entityNotFoundCallbackFired.current === urlEntityId) return;
|
|
298
|
+
|
|
299
|
+
entityNotFoundCallbackFired.current = urlEntityId;
|
|
300
|
+
onEntityNotFound(urlEntityId);
|
|
301
|
+
}, [urlEntityId, memoizedEntities, isLoading, onEntityNotFound]);
|
|
302
|
+
|
|
204
303
|
const cookieOpts = useMemo(
|
|
205
304
|
() => ({
|
|
206
305
|
path: "/",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createContext, type ReactNode, useContext, useEffect, useState } from "react";
|
|
1
|
+
import { createContext, type ReactNode, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
|
|
2
2
|
|
|
3
3
|
type SecondaryAction = {
|
|
4
4
|
label: string;
|
|
@@ -54,6 +54,37 @@ export function useFormFooterRegistration({
|
|
|
54
54
|
secondaryAction,
|
|
55
55
|
}: UseFormFooterRegistrationProps) {
|
|
56
56
|
const { setFormFooter } = useFormFooterContext();
|
|
57
|
+
const onSubmitRef = useRef(onSubmit);
|
|
58
|
+
const secondaryActionRef = useRef(secondaryAction);
|
|
59
|
+
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
onSubmitRef.current = onSubmit;
|
|
62
|
+
}, [onSubmit]);
|
|
63
|
+
|
|
64
|
+
useEffect(() => {
|
|
65
|
+
secondaryActionRef.current = secondaryAction;
|
|
66
|
+
}, [secondaryAction]);
|
|
67
|
+
|
|
68
|
+
const stableOnSubmit = useCallback(() => {
|
|
69
|
+
onSubmitRef.current?.();
|
|
70
|
+
}, []);
|
|
71
|
+
|
|
72
|
+
const hasSecondaryAction = !!secondaryAction;
|
|
73
|
+
const secondaryActionLabel = secondaryAction?.label;
|
|
74
|
+
const secondaryActionPending = secondaryAction?.isPending;
|
|
75
|
+
const stableSecondaryActionOnClick = useCallback(() => {
|
|
76
|
+
secondaryActionRef.current?.onClick();
|
|
77
|
+
}, []);
|
|
78
|
+
|
|
79
|
+
const stableSecondaryAction = useMemo(() => {
|
|
80
|
+
if (!hasSecondaryAction || !secondaryActionLabel) return undefined;
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
label: secondaryActionLabel,
|
|
84
|
+
isPending: secondaryActionPending,
|
|
85
|
+
onClick: stableSecondaryActionOnClick,
|
|
86
|
+
};
|
|
87
|
+
}, [hasSecondaryAction, secondaryActionLabel, secondaryActionPending, stableSecondaryActionOnClick]);
|
|
57
88
|
|
|
58
89
|
useEffect(() => {
|
|
59
90
|
setFormFooter({
|
|
@@ -61,12 +92,14 @@ export function useFormFooterRegistration({
|
|
|
61
92
|
isPending,
|
|
62
93
|
isDirty,
|
|
63
94
|
label,
|
|
64
|
-
onSubmit,
|
|
65
|
-
secondaryAction,
|
|
95
|
+
onSubmit: onSubmit ? stableOnSubmit : undefined,
|
|
96
|
+
secondaryAction: stableSecondaryAction,
|
|
66
97
|
});
|
|
98
|
+
}, [formId, isPending, isDirty, label, onSubmit, setFormFooter, stableOnSubmit, stableSecondaryAction]);
|
|
67
99
|
|
|
100
|
+
useEffect(() => {
|
|
68
101
|
return () => {
|
|
69
102
|
setFormFooter(null);
|
|
70
103
|
};
|
|
71
|
-
}, [
|
|
104
|
+
}, [setFormFooter]);
|
|
72
105
|
}
|