@spaceinvoices/react-ui 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +340 -0
- package/cli/dist/index.js +922 -0
- package/package.json +87 -0
- package/registry.json +600 -0
- package/spaceinvoices.schema.json +47 -0
- package/src/app.tsx +25 -0
- package/src/common/autocomplete.tsx +135 -0
- package/src/components/activities/activity-timeline.tsx +160 -0
- package/src/components/activities/index.ts +1 -0
- package/src/components/activities/locales/de.ts +30 -0
- package/src/components/activities/locales/sl.ts +30 -0
- package/src/components/advance-invoices/advance-invoices.hooks.ts +75 -0
- package/src/components/advance-invoices/create/create-advance-invoice-form.tsx +702 -0
- package/src/components/advance-invoices/create/locales/de.ts +29 -0
- package/src/components/advance-invoices/create/locales/sl.ts +25 -0
- package/src/components/advance-invoices/create/prepare-advance-invoice-submission.ts +74 -0
- package/src/components/advance-invoices/index.ts +5 -0
- package/src/components/advance-invoices/list/index.ts +3 -0
- package/src/components/advance-invoices/list/list-row-actions.tsx +119 -0
- package/src/components/advance-invoices/list/list-table.tsx +178 -0
- package/src/components/advance-invoices/list/locales/de.ts +32 -0
- package/src/components/advance-invoices/list/locales/sl.ts +32 -0
- package/src/components/advance-invoices/list/use-advance-invoice-download.ts +63 -0
- package/src/components/button-loader.tsx +11 -0
- package/src/components/combobox.tsx +96 -0
- package/src/components/company-registry/company-registry-autocomplete.tsx +151 -0
- package/src/components/company-registry/company-registry.hooks.ts +67 -0
- package/src/components/company-registry/index.ts +7 -0
- package/src/components/credit-notes/create/create-credit-note-form.tsx +332 -0
- package/src/components/credit-notes/create/index.ts +1 -0
- package/src/components/credit-notes/create/locales/de.ts +69 -0
- package/src/components/credit-notes/create/locales/sl.ts +67 -0
- package/src/components/credit-notes/credit-notes.hooks.ts +22 -0
- package/src/components/credit-notes/index.ts +10 -0
- package/src/components/credit-notes/list/index.ts +3 -0
- package/src/components/credit-notes/list/list-row-actions.tsx +116 -0
- package/src/components/credit-notes/list/list-table.tsx +183 -0
- package/src/components/credit-notes/list/locales/de.ts +33 -0
- package/src/components/credit-notes/list/locales/sl.ts +33 -0
- package/src/components/credit-notes/list/use-credit-note-download.ts +65 -0
- package/src/components/customers/create-customer-form/create-customer-form.tsx +134 -0
- package/src/components/customers/create-customer-form/locales/de.ts +20 -0
- package/src/components/customers/create-customer-form/locales/sl.ts +20 -0
- package/src/components/customers/customer-autocomplete.tsx +173 -0
- package/src/components/customers/customer-combobox.tsx +130 -0
- package/src/components/customers/customer-list-table/customer-list-row-actions.tsx +48 -0
- package/src/components/customers/customer-list-table/customer-list-table.tsx +124 -0
- package/src/components/customers/customer-list-table/index.ts +2 -0
- package/src/components/customers/customer-list-table/locales/de.ts +16 -0
- package/src/components/customers/customer-list-table/locales/sl.ts +16 -0
- package/src/components/customers/customers.hooks.test.ts +348 -0
- package/src/components/customers/customers.hooks.ts +57 -0
- package/src/components/customers/index.ts +5 -0
- package/src/components/dashboard/chart-empty-state.tsx +29 -0
- package/src/components/dashboard/collection-rate-card/collection-rate-card.tsx +80 -0
- package/src/components/dashboard/collection-rate-card/index.ts +4 -0
- package/src/components/dashboard/collection-rate-card/locales/sl.ts +3 -0
- package/src/components/dashboard/collection-rate-card/use-collection-rate.ts +74 -0
- package/src/components/dashboard/index.ts +54 -0
- package/src/components/dashboard/invoice-status-chart/index.ts +4 -0
- package/src/components/dashboard/invoice-status-chart/invoice-status-chart.tsx +130 -0
- package/src/components/dashboard/invoice-status-chart/locales/sl.ts +9 -0
- package/src/components/dashboard/invoice-status-chart/use-invoice-status.ts +105 -0
- package/src/components/dashboard/loading-card.tsx +19 -0
- package/src/components/dashboard/payment-methods-chart/index.ts +4 -0
- package/src/components/dashboard/payment-methods-chart/locales/sl.ts +12 -0
- package/src/components/dashboard/payment-methods-chart/payment-methods-chart.tsx +152 -0
- package/src/components/dashboard/payment-methods-chart/use-payment-methods.ts +50 -0
- package/src/components/dashboard/payment-trend-chart/index.ts +4 -0
- package/src/components/dashboard/payment-trend-chart/locales/sl.ts +5 -0
- package/src/components/dashboard/payment-trend-chart/payment-trend-chart.tsx +137 -0
- package/src/components/dashboard/payment-trend-chart/use-payment-trend.ts +92 -0
- package/src/components/dashboard/revenue-card.tsx +49 -0
- package/src/components/dashboard/revenue-trend-chart/index.ts +4 -0
- package/src/components/dashboard/revenue-trend-chart/locales/sl.ts +5 -0
- package/src/components/dashboard/revenue-trend-chart/revenue-trend-chart.tsx +137 -0
- package/src/components/dashboard/revenue-trend-chart/use-revenue-trend.ts +93 -0
- package/src/components/dashboard/shared/index.ts +5 -0
- package/src/components/dashboard/shared/use-revenue-data.ts +160 -0
- package/src/components/dashboard/shared/use-stats-counts.ts +89 -0
- package/src/components/dashboard/shared/use-stats-query.ts +38 -0
- package/src/components/dashboard/stat-card.tsx +41 -0
- package/src/components/dashboard/tax-collected-card/index.ts +2 -0
- package/src/components/dashboard/tax-collected-card/tax-collected-card.tsx +77 -0
- package/src/components/dashboard/tax-collected-card/use-tax-collected.ts +145 -0
- package/src/components/dashboard/top-customers-chart/index.ts +4 -0
- package/src/components/dashboard/top-customers-chart/locales/sl.ts +5 -0
- package/src/components/dashboard/top-customers-chart/top-customers-chart.tsx +130 -0
- package/src/components/dashboard/top-customers-chart/use-top-customers.ts +72 -0
- package/src/components/documents/create/document-add-item-form.tsx +379 -0
- package/src/components/documents/create/document-add-item-tax-rate-field.tsx +120 -0
- package/src/components/documents/create/document-details-section.tsx +597 -0
- package/src/components/documents/create/document-items-section.tsx +133 -0
- package/src/components/documents/create/document-recipient-section.tsx +101 -0
- package/src/components/documents/create/form-types.ts +36 -0
- package/src/components/documents/create/index.ts +9 -0
- package/src/components/documents/create/live-preview.tsx +235 -0
- package/src/components/documents/create/mark-as-paid-section.tsx +82 -0
- package/src/components/documents/create/prepare-document-submission.test.ts +132 -0
- package/src/components/documents/create/prepare-document-submission.ts +187 -0
- package/src/components/documents/create/prepare-preview-data.test.ts +155 -0
- package/src/components/documents/create/prepare-preview-data.ts +16 -0
- package/src/components/documents/create/smart-code-insert-button.tsx +139 -0
- package/src/components/documents/create/use-document-customer-form.ts +161 -0
- package/src/components/documents/document-preview.tsx +13 -0
- package/src/components/documents/documents.hooks.ts +146 -0
- package/src/components/documents/index.ts +23 -0
- package/src/components/documents/shared/document-preview-display.tsx +172 -0
- package/src/components/documents/shared/index.ts +3 -0
- package/src/components/documents/shared/scaled-document-preview.tsx +70 -0
- package/src/components/documents/shared/use-a4-scaling.ts +62 -0
- package/src/components/documents/types.ts +61 -0
- package/src/components/documents/view/document-actions-bar.tsx +328 -0
- package/src/components/documents/view/document-details-card.tsx +179 -0
- package/src/components/documents/view/document-payments-list.tsx +256 -0
- package/src/components/documents/view/index.ts +4 -0
- package/src/components/documents/view/locales/de.ts +85 -0
- package/src/components/documents/view/locales/sl.ts +84 -0
- package/src/components/documents/view/use-document-download.ts +125 -0
- package/src/components/entities/create-entity-form.tsx +105 -0
- package/src/components/entities/entities.hooks.ts +50 -0
- package/src/components/entities/entity-settings-form/email-template-variables-info.tsx +103 -0
- package/src/components/entities/entity-settings-form/entity-settings-form.tsx +1326 -0
- package/src/components/entities/entity-settings-form/image-upload-with-crop.tsx +222 -0
- package/src/components/entities/entity-settings-form/index.ts +2 -0
- package/src/components/entities/entity-settings-form/input-with-preview.tsx +190 -0
- package/src/components/entities/entity-settings-form/locales/de.ts +192 -0
- package/src/components/entities/entity-settings-form/locales/sl.ts +188 -0
- package/src/components/entities/furs-settings-form/furs-settings-form.tsx +410 -0
- package/src/components/entities/furs-settings-form/furs-settings.hooks.ts +320 -0
- package/src/components/entities/furs-settings-form/index.ts +3 -0
- package/src/components/entities/furs-settings-form/locales/de.ts +233 -0
- package/src/components/entities/furs-settings-form/locales/en.ts +194 -0
- package/src/components/entities/furs-settings-form/locales/sl.ts +196 -0
- package/src/components/entities/furs-settings-form/sections/certificate-settings-section.tsx +242 -0
- package/src/components/entities/furs-settings-form/sections/enable-fiscalization-section.tsx +139 -0
- package/src/components/entities/furs-settings-form/sections/general-settings-section.tsx +252 -0
- package/src/components/entities/furs-settings-form/sections/premises-management-section.tsx +370 -0
- package/src/components/entities/furs-settings-form/sections/register-premise-dialog.tsx +420 -0
- package/src/components/entities/keys.ts +2 -0
- package/src/components/entities/settings/branding-settings-form.tsx +274 -0
- package/src/components/entities/settings/company-settings-form.tsx +256 -0
- package/src/components/entities/settings/defaults-settings-form.tsx +501 -0
- package/src/components/entities/settings/email-settings-form.tsx +288 -0
- package/src/components/entities/settings/eslog-settings-form.tsx +113 -0
- package/src/components/entities/settings/index.ts +8 -0
- package/src/components/entities/settings/number-format-settings-form.tsx +244 -0
- package/src/components/entities/settings/pdf-template-selector/demo-invoice-data.ts +164 -0
- package/src/components/entities/settings/pdf-template-selector/index.ts +2 -0
- package/src/components/entities/settings/pdf-template-selector/locales/de.ts +18 -0
- package/src/components/entities/settings/pdf-template-selector/locales/sl.ts +18 -0
- package/src/components/entities/settings/pdf-template-selector/pdf-template-cards.tsx +49 -0
- package/src/components/entities/settings/settings-footer.tsx +16 -0
- package/src/components/entities/settings/tax-rules-settings-form.tsx +346 -0
- package/src/components/estimates/create/create-estimate-form.tsx +384 -0
- package/src/components/estimates/create/locales/de.ts +64 -0
- package/src/components/estimates/create/locales/sl.ts +63 -0
- package/src/components/estimates/create/prepare-estimate-submission.ts +39 -0
- package/src/components/estimates/create/use-estimate-customer-form.ts +5 -0
- package/src/components/estimates/estimates.hooks.ts +15 -0
- package/src/components/estimates/index.ts +6 -0
- package/src/components/estimates/list/index.ts +3 -0
- package/src/components/estimates/list/list-row-actions.tsx +103 -0
- package/src/components/estimates/list/list-table.tsx +171 -0
- package/src/components/estimates/list/locales/de.ts +26 -0
- package/src/components/estimates/list/locales/sl.ts +26 -0
- package/src/components/estimates/list/use-estimate-download.ts +63 -0
- package/src/components/export/document-export-form.tsx +288 -0
- package/src/components/export/index.ts +2 -0
- package/src/components/form/form-input.tsx +89 -0
- package/src/components/form/index.ts +1 -0
- package/src/components/invoices/create/create-invoice-form.tsx +852 -0
- package/src/components/invoices/create/eslog-validation.test.ts +242 -0
- package/src/components/invoices/create/eslog-validation.ts +208 -0
- package/src/components/invoices/create/locales/de.ts +118 -0
- package/src/components/invoices/create/locales/sl.ts +114 -0
- package/src/components/invoices/create/prepare-invoice-submission.test.ts +777 -0
- package/src/components/invoices/create/prepare-invoice-submission.ts +79 -0
- package/src/components/invoices/create/use-invoice-customer-form.ts +5 -0
- package/src/components/invoices/index.ts +9 -0
- package/src/components/invoices/invoices-furs.hooks.ts +28 -0
- package/src/components/invoices/invoices.hooks.ts +110 -0
- package/src/components/invoices/list/index.ts +3 -0
- package/src/components/invoices/list/list-row-actions.tsx +132 -0
- package/src/components/invoices/list/list-table.tsx +165 -0
- package/src/components/invoices/list/locales/de.ts +33 -0
- package/src/components/invoices/list/locales/sl.ts +33 -0
- package/src/components/invoices/list/use-invoice-download.ts +62 -0
- package/src/components/invoices/send-email-dialog/index.ts +1 -0
- package/src/components/invoices/send-email-dialog/locales/de.ts +18 -0
- package/src/components/invoices/send-email-dialog/locales/sl.ts +17 -0
- package/src/components/invoices/send-email-dialog/send-email-dialog.tsx +289 -0
- package/src/components/invoices/send-email-dialog.tsx +2 -0
- package/src/components/invoices/shared/index.ts +2 -0
- package/src/components/invoices/shared/scaled-document-preview.tsx +32 -0
- package/src/components/invoices/shared/use-a4-scaling.tsx +39 -0
- package/src/components/invoices/view/eslog-info-display.tsx +160 -0
- package/src/components/invoices/view/furs-info-display.tsx +213 -0
- package/src/components/items/create-item-form/create-item-form.tsx +155 -0
- package/src/components/items/create-item-form/locales/de.ts +14 -0
- package/src/components/items/create-item-form/locales/en.ts +9 -0
- package/src/components/items/create-item-form/locales/sl.ts +14 -0
- package/src/components/items/item-combobox.tsx +147 -0
- package/src/components/items/item-list-table/item-list-header.tsx +33 -0
- package/src/components/items/item-list-table/item-list-row-actions.tsx +48 -0
- package/src/components/items/item-list-table/item-list-row.tsx +32 -0
- package/src/components/items/item-list-table/item-list-table.tsx +76 -0
- package/src/components/items/item-list-table/locales/de.ts +10 -0
- package/src/components/items/item-list-table/locales/en.ts +10 -0
- package/src/components/items/item-list-table/locales/sl.ts +10 -0
- package/src/components/items/items.hooks.ts +63 -0
- package/src/components/loading-spinner.tsx +24 -0
- package/src/components/payments/create-payment-form/create-payment-form.tsx +222 -0
- package/src/components/payments/create-payment-form/locales/de.ts +20 -0
- package/src/components/payments/create-payment-form/locales/sl.ts +20 -0
- package/src/components/payments/edit-payment-form/edit-payment-form.tsx +230 -0
- package/src/components/payments/edit-payment-form/index.ts +1 -0
- package/src/components/payments/edit-payment-form/locales/de.ts +20 -0
- package/src/components/payments/edit-payment-form/locales/sl.ts +20 -0
- package/src/components/payments/index.ts +4 -0
- package/src/components/payments/list/index.ts +2 -0
- package/src/components/payments/list/list-row-actions.tsx +98 -0
- package/src/components/payments/list/list-table.tsx +186 -0
- package/src/components/payments/list/locales/de.ts +19 -0
- package/src/components/payments/list/locales/sl.ts +19 -0
- package/src/components/payments/payments.hooks.ts +15 -0
- package/src/components/request-logs/index.ts +3 -0
- package/src/components/request-logs/request-log-detail.tsx +242 -0
- package/src/components/request-logs/request-log-list-table.tsx +266 -0
- package/src/components/request-logs/request-logs-page.tsx +10 -0
- package/src/components/table/README.md +410 -0
- package/src/components/table/data-table.tsx +251 -0
- package/src/components/table/date-cell.tsx +35 -0
- package/src/components/table/filter-bar.tsx +114 -0
- package/src/components/table/filter-panel.tsx +407 -0
- package/src/components/table/hooks/use-table-fetch.ts +17 -0
- package/src/components/table/hooks/use-table-query.ts +36 -0
- package/src/components/table/hooks/use-table-state.ts +293 -0
- package/src/components/table/index.ts +35 -0
- package/src/components/table/search-input.tsx +85 -0
- package/src/components/table/sortable-header.tsx +56 -0
- package/src/components/table/table-empty-state.tsx +40 -0
- package/src/components/table/table-no-results.tsx +41 -0
- package/src/components/table/table-pagination.tsx +42 -0
- package/src/components/table/table-skeleton.tsx +54 -0
- package/src/components/table/types.ts +136 -0
- package/src/components/tax-reports/index.ts +1 -0
- package/src/components/tax-reports/kir-export-form.tsx +172 -0
- package/src/components/taxes/create-tax-form/create-tax-form.tsx +112 -0
- package/src/components/taxes/create-tax-form/locales/de.ts +8 -0
- package/src/components/taxes/create-tax-form/locales/en.ts +7 -0
- package/src/components/taxes/create-tax-form/locales/sl.ts +8 -0
- package/src/components/taxes/tax-list-table/locales/de.ts +11 -0
- package/src/components/taxes/tax-list-table/locales/en.ts +10 -0
- package/src/components/taxes/tax-list-table/locales/sl.ts +11 -0
- package/src/components/taxes/tax-list-table/tax-list-header.tsx +29 -0
- package/src/components/taxes/tax-list-table/tax-list-row-actions.tsx +43 -0
- package/src/components/taxes/tax-list-table/tax-list-row.tsx +46 -0
- package/src/components/taxes/tax-list-table/tax-list-table.tsx +59 -0
- package/src/components/taxes/taxes.hooks.ts +35 -0
- package/src/components/ui/alert-dialog.tsx +61 -0
- package/src/components/ui/alert.tsx +72 -0
- package/src/components/ui/badge.tsx +48 -0
- package/src/components/ui/breadcrumb.tsx +132 -0
- package/src/components/ui/button.tsx +61 -0
- package/src/components/ui/calendar.tsx +213 -0
- package/src/components/ui/card.tsx +94 -0
- package/src/components/ui/chart.tsx +380 -0
- package/src/components/ui/checkbox.tsx +27 -0
- package/src/components/ui/collapsible.tsx +56 -0
- package/src/components/ui/command.tsx +187 -0
- package/src/components/ui/dialog.tsx +187 -0
- package/src/components/ui/drawer.tsx +123 -0
- package/src/components/ui/dropdown-menu.tsx +291 -0
- package/src/components/ui/form.tsx +166 -0
- package/src/components/ui/input-group.tsx +149 -0
- package/src/components/ui/input.tsx +20 -0
- package/src/components/ui/label.tsx +18 -0
- package/src/components/ui/loading-spinner.tsx +16 -0
- package/src/components/ui/popover.tsx +108 -0
- package/src/components/ui/radio-group.tsx +37 -0
- package/src/components/ui/select.tsx +200 -0
- package/src/components/ui/separator.tsx +23 -0
- package/src/components/ui/sheet.tsx +145 -0
- package/src/components/ui/sidebar.tsx +771 -0
- package/src/components/ui/skeleton.tsx +13 -0
- package/src/components/ui/sonner.tsx +60 -0
- package/src/components/ui/spinner.tsx +10 -0
- package/src/components/ui/sticky-form-footer.tsx +55 -0
- package/src/components/ui/switch.tsx +30 -0
- package/src/components/ui/table.tsx +101 -0
- package/src/components/ui/tabs.tsx +80 -0
- package/src/components/ui/textarea.tsx +18 -0
- package/src/components/ui/tooltip.tsx +89 -0
- package/src/components/wl-subscription/index.ts +2 -0
- package/src/components/wl-subscription/locked-feature.tsx +173 -0
- package/src/components/wl-subscription/upgrade-modal.tsx +209 -0
- package/src/frontend.tsx +28 -0
- package/src/generate-schemas.ts +265 -0
- package/src/generated/schemas/advanceinvoice.ts +177 -0
- package/src/generated/schemas/creditnote.ts +187 -0
- package/src/generated/schemas/customer.ts +29 -0
- package/src/generated/schemas/entity.ts +252 -0
- package/src/generated/schemas/estimate.ts +159 -0
- package/src/generated/schemas/furssettings.ts +25 -0
- package/src/generated/schemas/index.ts +24 -0
- package/src/generated/schemas/invoice.ts +167 -0
- package/src/generated/schemas/item.ts +38 -0
- package/src/generated/schemas/payment.ts +44 -0
- package/src/generated/schemas/previewadvanceinvoice_body.ts +354 -0
- package/src/generated/schemas/previewestimate_body.ts +309 -0
- package/src/generated/schemas/registerfursmovablepremise_body.ts +22 -0
- package/src/generated/schemas/registerfursrealestatepremise_body.ts +32 -0
- package/src/generated/schemas/renderdocument_body.ts +594 -0
- package/src/generated/schemas/sendemail_body.ts +26 -0
- package/src/generated/schemas/startpdfexport_body.ts +20 -0
- package/src/generated/schemas/tax.ts +48 -0
- package/src/generated/schemas/uploadfile_body.ts +23 -0
- package/src/generated/schemas/uploadfurscertificate_body.ts +20 -0
- package/src/generated/schemas/userfurssettings.ts +19 -0
- package/src/hooks/create-resource-hooks.test.ts +483 -0
- package/src/hooks/create-resource-hooks.ts +300 -0
- package/src/hooks/use-debounce.ts +12 -0
- package/src/hooks/use-duplicate-document.ts +185 -0
- package/src/hooks/use-media-query.tsx +19 -0
- package/src/hooks/use-mobile.ts +39 -0
- package/src/hooks/use-next-document-number.ts +57 -0
- package/src/hooks/use-resource-mutation.ts +118 -0
- package/src/hooks/use-vies-check.ts +130 -0
- package/src/index.css +11 -0
- package/src/index.html +13 -0
- package/src/index.tsx +12 -0
- package/src/lib/auth.ts +4 -0
- package/src/lib/browser-cookies.ts +70 -0
- package/src/lib/constants.ts +287 -0
- package/src/lib/cookies.ts +36 -0
- package/src/lib/schemas/advance-invoice.ts +43 -0
- package/src/lib/schemas/credit-note.ts +32 -0
- package/src/lib/schemas/estimate.ts +31 -0
- package/src/lib/schemas/index.ts +18 -0
- package/src/lib/schemas/invoice.ts +43 -0
- package/src/lib/schemas/shared.ts +79 -0
- package/src/lib/translation.ts +38 -0
- package/src/lib/utils.ts +6 -0
- package/src/providers/entities-context.tsx +41 -0
- package/src/providers/entities-provider.tsx +201 -0
- package/src/providers/form-footer-context.tsx +72 -0
- package/src/providers/sdk-provider.tsx +164 -0
- package/src/providers/white-label-provider.tsx +91 -0
- package/src/providers/wl-subscription-provider.tsx +277 -0
- package/src/utils/string-helpers.ts +111 -0
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
import type SDK from "@spaceinvoices/js-sdk";
|
|
2
|
+
import type { UseMutationOptions } from "@tanstack/react-query";
|
|
3
|
+
|
|
4
|
+
import { useQueryClient } from "@tanstack/react-query";
|
|
5
|
+
import { useResourceMutation } from "./use-resource-mutation";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Options for resource mutation hooks
|
|
9
|
+
*/
|
|
10
|
+
type ResourceMutationHookOptions<TData, TError, TVariables = any> = {
|
|
11
|
+
entityId?: string | null;
|
|
12
|
+
accountId?: string | null;
|
|
13
|
+
onSuccess?: (data: TData, variables: TVariables, context: unknown) => void;
|
|
14
|
+
onError?: (error: TError, variables: TVariables, context: unknown) => void;
|
|
15
|
+
mutationOptions?: Omit<UseMutationOptions<TData, TError, any, unknown>, "mutationFn" | "onSuccess" | "onError">;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Build query key with optional filters
|
|
20
|
+
*/
|
|
21
|
+
function buildQueryKey(
|
|
22
|
+
baseKey: string,
|
|
23
|
+
filters?: { entityId?: string | null; accountId?: string | null },
|
|
24
|
+
): [string, Record<string, string>?] {
|
|
25
|
+
if (!filters?.entityId && !filters?.accountId) {
|
|
26
|
+
return [baseKey];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const params: Record<string, string> = {};
|
|
30
|
+
if (filters.entityId) params.entityId = filters.entityId;
|
|
31
|
+
if (filters.accountId) params.accountId = filters.accountId;
|
|
32
|
+
|
|
33
|
+
return [baseKey, params];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Invalidate resource-specific queries
|
|
38
|
+
*/
|
|
39
|
+
function invalidateResourceQueries(
|
|
40
|
+
queryClient: ReturnType<typeof useQueryClient>,
|
|
41
|
+
cacheKey: string,
|
|
42
|
+
resourceId: string,
|
|
43
|
+
filters?: { entityId?: string | null; accountId?: string | null },
|
|
44
|
+
): void {
|
|
45
|
+
const detailKey = `${cacheKey}-${resourceId}`;
|
|
46
|
+
queryClient.invalidateQueries({ queryKey: buildQueryKey(detailKey, filters) });
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Factory function to create resource-specific CRUD hooks
|
|
51
|
+
*
|
|
52
|
+
* @param resourceName - The name of the resource in the SDK (e.g., 'customers')
|
|
53
|
+
* @param cacheKey - The cache key for the resource queries
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```ts
|
|
57
|
+
* const { useCreateResource, useUpdateResource, useDeleteResource } =
|
|
58
|
+
* createResourceHooks<Customer, CreateCustomerRequest['data']>('customers', 'customers');
|
|
59
|
+
*
|
|
60
|
+
* // Usage
|
|
61
|
+
* const createCustomer = useCreateResource();
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
export function createResourceHooks<
|
|
65
|
+
TResource extends { id: string },
|
|
66
|
+
TCreateData = unknown,
|
|
67
|
+
TUpdateData = Partial<Omit<TResource, "id">>,
|
|
68
|
+
>(resourceName: keyof SDK, cacheKey: string) {
|
|
69
|
+
/**
|
|
70
|
+
* Hook for creating a new resource
|
|
71
|
+
*/
|
|
72
|
+
function useCreateResource<TError = Error>(
|
|
73
|
+
options: ResourceMutationHookOptions<TResource, TError, TCreateData> = {},
|
|
74
|
+
) {
|
|
75
|
+
const queryClient = useQueryClient();
|
|
76
|
+
|
|
77
|
+
return useResourceMutation<TResource, TError, TCreateData>({
|
|
78
|
+
resourceName,
|
|
79
|
+
methodName: "create",
|
|
80
|
+
cacheKey,
|
|
81
|
+
entityId: options.entityId,
|
|
82
|
+
accountId: options.accountId,
|
|
83
|
+
mutationOptions: {
|
|
84
|
+
onMutate: async (variables) => {
|
|
85
|
+
// Cancel any outgoing refetches to avoid overwriting optimistic update
|
|
86
|
+
const listQueryKey = buildQueryKey(cacheKey, {
|
|
87
|
+
entityId: options.entityId,
|
|
88
|
+
accountId: options.accountId,
|
|
89
|
+
});
|
|
90
|
+
await queryClient.cancelQueries({ queryKey: listQueryKey });
|
|
91
|
+
|
|
92
|
+
// Snapshot the previous value
|
|
93
|
+
const previousData = queryClient.getQueryData(listQueryKey);
|
|
94
|
+
|
|
95
|
+
// Optimistically add the new resource to the cache
|
|
96
|
+
queryClient.setQueryData(listQueryKey, (oldData: any) => {
|
|
97
|
+
if (!oldData?.data) return oldData;
|
|
98
|
+
|
|
99
|
+
// Create temporary resource for optimistic update
|
|
100
|
+
// Use 'unknown' as intermediate to safely cast
|
|
101
|
+
const tempResource = {
|
|
102
|
+
...variables,
|
|
103
|
+
id: `temp-${Date.now()}`,
|
|
104
|
+
created_at: new Date().toISOString(),
|
|
105
|
+
updated_at: new Date().toISOString(),
|
|
106
|
+
} as unknown as TResource;
|
|
107
|
+
|
|
108
|
+
return {
|
|
109
|
+
...oldData,
|
|
110
|
+
data: [tempResource, ...oldData.data],
|
|
111
|
+
pagination: oldData.pagination
|
|
112
|
+
? {
|
|
113
|
+
...oldData.pagination,
|
|
114
|
+
total: oldData.pagination.total + 1,
|
|
115
|
+
}
|
|
116
|
+
: undefined,
|
|
117
|
+
};
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
return { previousData };
|
|
121
|
+
},
|
|
122
|
+
onSuccess: options.onSuccess,
|
|
123
|
+
onError: (error, variables, context: any) => {
|
|
124
|
+
// Rollback on error
|
|
125
|
+
if (context?.previousData) {
|
|
126
|
+
const listQueryKey = buildQueryKey(cacheKey, {
|
|
127
|
+
entityId: options.entityId,
|
|
128
|
+
accountId: options.accountId,
|
|
129
|
+
});
|
|
130
|
+
queryClient.setQueryData(listQueryKey, context.previousData);
|
|
131
|
+
}
|
|
132
|
+
options.onError?.(error, variables, context);
|
|
133
|
+
},
|
|
134
|
+
onSettled: () => {
|
|
135
|
+
// Always refetch after error or success to ensure consistency
|
|
136
|
+
const listQueryKey = buildQueryKey(cacheKey, {
|
|
137
|
+
entityId: options.entityId,
|
|
138
|
+
accountId: options.accountId,
|
|
139
|
+
});
|
|
140
|
+
queryClient.invalidateQueries({ queryKey: listQueryKey });
|
|
141
|
+
},
|
|
142
|
+
...options.mutationOptions,
|
|
143
|
+
},
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Hook for updating an existing resource
|
|
149
|
+
*/
|
|
150
|
+
function useUpdateResource<TError = Error>(
|
|
151
|
+
options: ResourceMutationHookOptions<TResource, TError, { id: string; data: TUpdateData }> = {},
|
|
152
|
+
) {
|
|
153
|
+
const queryClient = useQueryClient();
|
|
154
|
+
|
|
155
|
+
return useResourceMutation<TResource, TError, { id: string; data: TUpdateData }>({
|
|
156
|
+
resourceName,
|
|
157
|
+
methodName: "update",
|
|
158
|
+
cacheKey: [cacheKey, `${cacheKey}-detail`],
|
|
159
|
+
entityId: options.entityId,
|
|
160
|
+
accountId: options.accountId,
|
|
161
|
+
mutationOptions: {
|
|
162
|
+
onMutate: async (variables) => {
|
|
163
|
+
const listQueryKey = buildQueryKey(cacheKey, {
|
|
164
|
+
entityId: options.entityId,
|
|
165
|
+
accountId: options.accountId,
|
|
166
|
+
});
|
|
167
|
+
const detailKey = `${cacheKey}-${variables.id}`;
|
|
168
|
+
const detailQueryKey = buildQueryKey(detailKey, {
|
|
169
|
+
entityId: options.entityId,
|
|
170
|
+
accountId: options.accountId,
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// Cancel outgoing refetches
|
|
174
|
+
await queryClient.cancelQueries({ queryKey: listQueryKey });
|
|
175
|
+
await queryClient.cancelQueries({ queryKey: detailQueryKey });
|
|
176
|
+
|
|
177
|
+
// Snapshot previous values
|
|
178
|
+
const previousListData = queryClient.getQueryData(listQueryKey);
|
|
179
|
+
const previousDetailData = queryClient.getQueryData(detailQueryKey);
|
|
180
|
+
|
|
181
|
+
// Optimistically update list cache
|
|
182
|
+
queryClient.setQueryData(listQueryKey, (oldData: any) => {
|
|
183
|
+
if (!oldData?.data) return oldData;
|
|
184
|
+
|
|
185
|
+
return {
|
|
186
|
+
...oldData,
|
|
187
|
+
data: oldData.data.map((resource: TResource) =>
|
|
188
|
+
resource.id === variables.id
|
|
189
|
+
? {
|
|
190
|
+
...resource,
|
|
191
|
+
...variables.data,
|
|
192
|
+
updated_at: new Date().toISOString(),
|
|
193
|
+
}
|
|
194
|
+
: resource,
|
|
195
|
+
),
|
|
196
|
+
};
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
// Optimistically update detail cache
|
|
200
|
+
queryClient.setQueryData(detailQueryKey, (oldData: any) => {
|
|
201
|
+
if (!oldData) return oldData;
|
|
202
|
+
return {
|
|
203
|
+
...oldData,
|
|
204
|
+
...variables.data,
|
|
205
|
+
updated_at: new Date().toISOString(),
|
|
206
|
+
};
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
return { previousListData, previousDetailData };
|
|
210
|
+
},
|
|
211
|
+
onSuccess: (data, variables, context) => {
|
|
212
|
+
// Invalidate the specific resource detail cache
|
|
213
|
+
invalidateResourceQueries(queryClient, cacheKey, variables.id, {
|
|
214
|
+
entityId: options.entityId,
|
|
215
|
+
accountId: options.accountId,
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
options.onSuccess?.(data, variables, context);
|
|
219
|
+
},
|
|
220
|
+
onError: (error, variables, context: any) => {
|
|
221
|
+
// Rollback on error
|
|
222
|
+
if (context?.previousListData) {
|
|
223
|
+
const listQueryKey = buildQueryKey(cacheKey, {
|
|
224
|
+
entityId: options.entityId,
|
|
225
|
+
accountId: options.accountId,
|
|
226
|
+
});
|
|
227
|
+
queryClient.setQueryData(listQueryKey, context.previousListData);
|
|
228
|
+
}
|
|
229
|
+
if (context?.previousDetailData) {
|
|
230
|
+
const detailKey = `${cacheKey}-${variables.id}`;
|
|
231
|
+
const detailQueryKey = buildQueryKey(detailKey, {
|
|
232
|
+
entityId: options.entityId,
|
|
233
|
+
accountId: options.accountId,
|
|
234
|
+
});
|
|
235
|
+
queryClient.setQueryData(detailQueryKey, context.previousDetailData);
|
|
236
|
+
}
|
|
237
|
+
options.onError?.(error, variables, context);
|
|
238
|
+
},
|
|
239
|
+
onSettled: () => {
|
|
240
|
+
// Always refetch to ensure consistency
|
|
241
|
+
const listQueryKey = buildQueryKey(cacheKey, {
|
|
242
|
+
entityId: options.entityId,
|
|
243
|
+
accountId: options.accountId,
|
|
244
|
+
});
|
|
245
|
+
queryClient.invalidateQueries({ queryKey: listQueryKey });
|
|
246
|
+
},
|
|
247
|
+
...options.mutationOptions,
|
|
248
|
+
},
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Hook for deleting a resource
|
|
254
|
+
*/
|
|
255
|
+
function useDeleteResource<TError = Error>(options: ResourceMutationHookOptions<void, TError, { id: string }> = {}) {
|
|
256
|
+
const queryClient = useQueryClient();
|
|
257
|
+
|
|
258
|
+
return useResourceMutation<void, TError, { id: string }>({
|
|
259
|
+
resourceName,
|
|
260
|
+
methodName: "delete",
|
|
261
|
+
cacheKey,
|
|
262
|
+
entityId: options.entityId,
|
|
263
|
+
accountId: options.accountId,
|
|
264
|
+
mutationOptions: {
|
|
265
|
+
onSuccess: (data, variables, context) => {
|
|
266
|
+
// Invalidate the specific resource detail cache
|
|
267
|
+
invalidateResourceQueries(queryClient, cacheKey, variables.id, {
|
|
268
|
+
entityId: options.entityId,
|
|
269
|
+
accountId: options.accountId,
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
// Optimistically remove from list cache
|
|
273
|
+
const listQueryKey = buildQueryKey(cacheKey, {
|
|
274
|
+
entityId: options.entityId,
|
|
275
|
+
accountId: options.accountId,
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
queryClient.setQueriesData({ queryKey: listQueryKey }, (oldData: any) => {
|
|
279
|
+
if (!oldData?.data) return oldData;
|
|
280
|
+
|
|
281
|
+
return {
|
|
282
|
+
...oldData,
|
|
283
|
+
data: oldData.data.filter((resource: TResource) => resource.id !== variables.id),
|
|
284
|
+
};
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
options.onSuccess?.(data, variables, context);
|
|
288
|
+
},
|
|
289
|
+
onError: options.onError,
|
|
290
|
+
...options.mutationOptions,
|
|
291
|
+
},
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
return {
|
|
296
|
+
useCreateResource,
|
|
297
|
+
useUpdateResource,
|
|
298
|
+
useDeleteResource,
|
|
299
|
+
};
|
|
300
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
|
|
3
|
+
export function useDebounce<T>(value: T, delay: number): T {
|
|
4
|
+
const [debouncedValue, setDebouncedValue] = useState<T>(value);
|
|
5
|
+
|
|
6
|
+
useEffect(() => {
|
|
7
|
+
const timer = setTimeout(() => setDebouncedValue(value), delay);
|
|
8
|
+
return () => clearTimeout(timer);
|
|
9
|
+
}, [value, delay]);
|
|
10
|
+
|
|
11
|
+
return debouncedValue;
|
|
12
|
+
}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AdvanceInvoiceWithItems,
|
|
3
|
+
CreateAdvanceInvoiceRequest,
|
|
4
|
+
CreateCreditNoteRequest,
|
|
5
|
+
CreateEstimateRequest,
|
|
6
|
+
CreateInvoiceRequest,
|
|
7
|
+
CreditNoteWithItems,
|
|
8
|
+
EstimateWithItems,
|
|
9
|
+
InvoiceWithItems,
|
|
10
|
+
} from "@spaceinvoices/js-sdk";
|
|
11
|
+
import { useQuery } from "@tanstack/react-query";
|
|
12
|
+
|
|
13
|
+
import { useEntities } from "@/ui/providers/entities-context";
|
|
14
|
+
import { useSDK } from "@/ui/providers/sdk-provider";
|
|
15
|
+
|
|
16
|
+
export type DocumentType = "invoice" | "estimate" | "credit_note" | "advance_invoice";
|
|
17
|
+
type Document = InvoiceWithItems | EstimateWithItems | CreditNoteWithItems | AdvanceInvoiceWithItems;
|
|
18
|
+
type CreateRequest =
|
|
19
|
+
| CreateInvoiceRequest
|
|
20
|
+
| CreateEstimateRequest
|
|
21
|
+
| CreateCreditNoteRequest
|
|
22
|
+
| CreateAdvanceInvoiceRequest;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Get document type from ID prefix
|
|
26
|
+
*/
|
|
27
|
+
export function getDocumentTypeFromId(id: string): DocumentType | null {
|
|
28
|
+
if (id.startsWith("inv_")) return "invoice";
|
|
29
|
+
if (id.startsWith("est_")) return "estimate";
|
|
30
|
+
if (id.startsWith("cn_")) return "credit_note";
|
|
31
|
+
if (id.startsWith("adv_")) return "advance_invoice";
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Get allowed target types for duplication/conversion
|
|
37
|
+
*/
|
|
38
|
+
export function getAllowedDuplicateTargets(sourceType: DocumentType): DocumentType[] {
|
|
39
|
+
switch (sourceType) {
|
|
40
|
+
case "invoice":
|
|
41
|
+
return ["invoice", "credit_note"];
|
|
42
|
+
case "estimate":
|
|
43
|
+
return ["estimate", "invoice"];
|
|
44
|
+
case "credit_note":
|
|
45
|
+
return ["credit_note"];
|
|
46
|
+
case "advance_invoice":
|
|
47
|
+
return ["advance_invoice", "invoice"];
|
|
48
|
+
default:
|
|
49
|
+
return [];
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Transform a source document into form-compatible initial values
|
|
55
|
+
* Copies relevant fields and resets computed/generated ones
|
|
56
|
+
*/
|
|
57
|
+
function transformDocumentForDuplication(source: Document, _targetType: DocumentType): Partial<CreateRequest> {
|
|
58
|
+
// Transform items - copy only the fields needed for creation
|
|
59
|
+
const items = source.items?.map((item: (typeof source.items)[number]) => ({
|
|
60
|
+
name: item.name,
|
|
61
|
+
description: item.description,
|
|
62
|
+
quantity: item.quantity,
|
|
63
|
+
// Use gross_price if set, otherwise use price. The form uses is_gross_price as a UI toggle.
|
|
64
|
+
price: (item as { gross_price?: number }).gross_price ?? item.price,
|
|
65
|
+
// Copy tax references (tax_id), not computed tax data
|
|
66
|
+
taxes: item.taxes?.map((tax: { tax_id?: string }) => ({ tax_id: tax.tax_id })),
|
|
67
|
+
// Derive is_gross_price from whether gross_price is set
|
|
68
|
+
gross_price: (item as { gross_price?: number }).gross_price,
|
|
69
|
+
}));
|
|
70
|
+
|
|
71
|
+
// Build customer data - always copy if available (form needs this for display)
|
|
72
|
+
const customerData = source.customer
|
|
73
|
+
? {
|
|
74
|
+
name: source.customer.name,
|
|
75
|
+
address: source.customer.address,
|
|
76
|
+
address_2: source.customer.address_2,
|
|
77
|
+
city: source.customer.city,
|
|
78
|
+
post_code: source.customer.post_code,
|
|
79
|
+
country: source.customer.country,
|
|
80
|
+
country_code: source.customer.country_code,
|
|
81
|
+
tax_number: source.customer.tax_number,
|
|
82
|
+
email: source.customer.email,
|
|
83
|
+
}
|
|
84
|
+
: undefined;
|
|
85
|
+
|
|
86
|
+
// Build base duplicate data
|
|
87
|
+
const baseData: Partial<CreateRequest> = {
|
|
88
|
+
// Customer - always pass both customer_id AND customer data when available
|
|
89
|
+
// The form needs customer data for display, even when customer_id is set
|
|
90
|
+
...(source.customer_id ? { customer_id: source.customer_id } : {}),
|
|
91
|
+
...(customerData ? { customer: customerData } : {}),
|
|
92
|
+
// Items
|
|
93
|
+
items,
|
|
94
|
+
// Currency
|
|
95
|
+
currency_code: source.currency_code,
|
|
96
|
+
// Notes
|
|
97
|
+
note: source.note,
|
|
98
|
+
payment_terms: source.payment_terms,
|
|
99
|
+
// Date - use today's date as ISO string
|
|
100
|
+
date: new Date().toISOString(),
|
|
101
|
+
// Number - leave empty for auto-generation
|
|
102
|
+
// Do NOT copy: number, totals, taxes, payments, furs, eslog, vies, shareable_id
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
return baseData;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export type UseDuplicateDocumentOptions = {
|
|
109
|
+
/** Source document ID to duplicate from */
|
|
110
|
+
sourceId: string | undefined;
|
|
111
|
+
/** Target document type (may differ from source for conversions) */
|
|
112
|
+
targetType: DocumentType;
|
|
113
|
+
/** Whether to enable the query */
|
|
114
|
+
enabled?: boolean;
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
export type UseDuplicateDocumentResult = {
|
|
118
|
+
/** Transformed initial values for the form */
|
|
119
|
+
initialValues: Partial<CreateRequest> | undefined;
|
|
120
|
+
/** Loading state */
|
|
121
|
+
isLoading: boolean;
|
|
122
|
+
/** Error if fetch failed */
|
|
123
|
+
error: Error | null;
|
|
124
|
+
/** Source document type */
|
|
125
|
+
sourceType: DocumentType | null;
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Hook to fetch and transform a document for duplication
|
|
130
|
+
*
|
|
131
|
+
* @example
|
|
132
|
+
* ```tsx
|
|
133
|
+
* const { initialValues, isLoading } = useDuplicateDocument({
|
|
134
|
+
* sourceId: searchParams.duplicateFrom,
|
|
135
|
+
* targetType: 'invoice',
|
|
136
|
+
* });
|
|
137
|
+
* ```
|
|
138
|
+
*/
|
|
139
|
+
export function useDuplicateDocument({
|
|
140
|
+
sourceId,
|
|
141
|
+
targetType,
|
|
142
|
+
enabled = true,
|
|
143
|
+
}: UseDuplicateDocumentOptions): UseDuplicateDocumentResult {
|
|
144
|
+
const { sdk } = useSDK();
|
|
145
|
+
const { activeEntity } = useEntities();
|
|
146
|
+
|
|
147
|
+
const sourceType = sourceId ? getDocumentTypeFromId(sourceId) : null;
|
|
148
|
+
|
|
149
|
+
const query = useQuery({
|
|
150
|
+
queryKey: ["duplicate-document", sourceId, targetType, activeEntity?.id],
|
|
151
|
+
queryFn: async () => {
|
|
152
|
+
if (!sourceId || !activeEntity?.id || !sourceType) {
|
|
153
|
+
throw new Error("Source document ID and entity ID are required");
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Fetch source document based on its type
|
|
157
|
+
let source: Document;
|
|
158
|
+
if (sourceType === "invoice") {
|
|
159
|
+
source = await sdk.invoices.get(sourceId, undefined, { entity_id: activeEntity.id });
|
|
160
|
+
} else if (sourceType === "estimate") {
|
|
161
|
+
// estimates.get only takes 2 args (no params)
|
|
162
|
+
source = await sdk.estimates.get(sourceId, { entity_id: activeEntity.id });
|
|
163
|
+
} else if (sourceType === "advance_invoice") {
|
|
164
|
+
source = await sdk.advanceInvoices.get(sourceId, undefined, { entity_id: activeEntity.id });
|
|
165
|
+
} else {
|
|
166
|
+
// Credit note
|
|
167
|
+
source = await sdk.creditNotes.get(sourceId, undefined, { entity_id: activeEntity.id });
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (!source) {
|
|
171
|
+
throw new Error("Source document not found");
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return transformDocumentForDuplication(source, targetType);
|
|
175
|
+
},
|
|
176
|
+
enabled: enabled && !!sourceId && !!activeEntity?.id && !!sourceType,
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
return {
|
|
180
|
+
initialValues: query.data,
|
|
181
|
+
isLoading: query.isLoading,
|
|
182
|
+
error: query.error,
|
|
183
|
+
sourceType,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
export function useMediaQuery(query: string) {
|
|
4
|
+
const [value, setValue] = React.useState(false);
|
|
5
|
+
|
|
6
|
+
React.useEffect(() => {
|
|
7
|
+
function onChange(event: MediaQueryListEvent) {
|
|
8
|
+
setValue(event.matches);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const result = matchMedia(query);
|
|
12
|
+
result.addEventListener("change", onChange);
|
|
13
|
+
setValue(result.matches);
|
|
14
|
+
|
|
15
|
+
return () => result.removeEventListener("change", onChange);
|
|
16
|
+
}, [query]);
|
|
17
|
+
|
|
18
|
+
return value;
|
|
19
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
const MOBILE_BREAKPOINT = 768;
|
|
4
|
+
const SMALL_SCREEN_BREAKPOINT = 1024;
|
|
5
|
+
|
|
6
|
+
export function useIsMobile() {
|
|
7
|
+
const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined);
|
|
8
|
+
|
|
9
|
+
React.useEffect(() => {
|
|
10
|
+
const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
|
|
11
|
+
const onChange = () => {
|
|
12
|
+
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
|
|
13
|
+
};
|
|
14
|
+
mql.addEventListener("change", onChange);
|
|
15
|
+
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
|
|
16
|
+
return () => mql.removeEventListener("change", onChange);
|
|
17
|
+
}, []);
|
|
18
|
+
|
|
19
|
+
return !!isMobile;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function useIsSmallScreen() {
|
|
23
|
+
const [isSmallScreen, setIsSmallScreen] = React.useState<boolean | undefined>(undefined);
|
|
24
|
+
|
|
25
|
+
React.useEffect(() => {
|
|
26
|
+
const checkSize = () => {
|
|
27
|
+
const width = window.innerWidth;
|
|
28
|
+
setIsSmallScreen(width >= MOBILE_BREAKPOINT && width < SMALL_SCREEN_BREAKPOINT);
|
|
29
|
+
};
|
|
30
|
+
const mql = window.matchMedia(
|
|
31
|
+
`(min-width: ${MOBILE_BREAKPOINT}px) and (max-width: ${SMALL_SCREEN_BREAKPOINT - 1}px)`,
|
|
32
|
+
);
|
|
33
|
+
mql.addEventListener("change", checkSize);
|
|
34
|
+
checkSize();
|
|
35
|
+
return () => mql.removeEventListener("change", checkSize);
|
|
36
|
+
}, []);
|
|
37
|
+
|
|
38
|
+
return !!isSmallScreen;
|
|
39
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { useQuery } from "@tanstack/react-query";
|
|
2
|
+
|
|
3
|
+
import { useSDK } from "@/ui/providers/sdk-provider";
|
|
4
|
+
|
|
5
|
+
export const NEXT_DOCUMENT_NUMBER_CACHE_KEY = "next-document-number";
|
|
6
|
+
|
|
7
|
+
export type DocumentType = "invoice" | "estimate" | "credit_note" | "advance_invoice";
|
|
8
|
+
|
|
9
|
+
/** Response type for next document number preview */
|
|
10
|
+
export type NextDocumentNumberResponse = {
|
|
11
|
+
number: string | null;
|
|
12
|
+
furs: {
|
|
13
|
+
business_premise_name: string;
|
|
14
|
+
electronic_device_name: string;
|
|
15
|
+
} | null;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Hook to fetch the next document number preview
|
|
20
|
+
* Does not increment the sequence - purely for preview purposes
|
|
21
|
+
*
|
|
22
|
+
* Uses sdk.documents.getNextNumber() - the shared documents API
|
|
23
|
+
*/
|
|
24
|
+
export function useNextDocumentNumber(
|
|
25
|
+
entityId: string,
|
|
26
|
+
type: DocumentType,
|
|
27
|
+
options?: {
|
|
28
|
+
businessPremiseName?: string;
|
|
29
|
+
electronicDeviceName?: string;
|
|
30
|
+
enabled?: boolean;
|
|
31
|
+
},
|
|
32
|
+
) {
|
|
33
|
+
const { sdk } = useSDK();
|
|
34
|
+
|
|
35
|
+
return useQuery<NextDocumentNumberResponse>({
|
|
36
|
+
queryKey: [
|
|
37
|
+
NEXT_DOCUMENT_NUMBER_CACHE_KEY,
|
|
38
|
+
entityId,
|
|
39
|
+
type,
|
|
40
|
+
options?.businessPremiseName,
|
|
41
|
+
options?.electronicDeviceName,
|
|
42
|
+
],
|
|
43
|
+
queryFn: async () => {
|
|
44
|
+
const response = await sdk.documents.getNextNumber(
|
|
45
|
+
{
|
|
46
|
+
type,
|
|
47
|
+
business_premise_name: options?.businessPremiseName,
|
|
48
|
+
electronic_device_name: options?.electronicDeviceName,
|
|
49
|
+
},
|
|
50
|
+
{ entity_id: entityId },
|
|
51
|
+
);
|
|
52
|
+
return response;
|
|
53
|
+
},
|
|
54
|
+
enabled: options?.enabled !== false && !!entityId && !!sdk?.documents,
|
|
55
|
+
staleTime: 0, // Always refetch when form opens
|
|
56
|
+
});
|
|
57
|
+
}
|