@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,209 @@
|
|
|
1
|
+
import { Check, Crown, Sparkles, Zap } from "lucide-react";
|
|
2
|
+
|
|
3
|
+
import { type GatedFeature, useWLSubscription, type WhiteLabelPlan } from "../../providers/wl-subscription-provider";
|
|
4
|
+
import { Button } from "../ui/button";
|
|
5
|
+
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "../ui/dialog";
|
|
6
|
+
|
|
7
|
+
type UpgradeModalProps = {
|
|
8
|
+
isOpen: boolean;
|
|
9
|
+
onClose: () => void;
|
|
10
|
+
/** Optional feature that triggered the modal */
|
|
11
|
+
feature?: GatedFeature;
|
|
12
|
+
/** Optional callback when upgrade is clicked */
|
|
13
|
+
onUpgrade?: (planSlug: string) => void;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* UpgradeModal component
|
|
18
|
+
*
|
|
19
|
+
* Shows available plans and allows users to upgrade their subscription.
|
|
20
|
+
* Highlights the feature that triggered the modal and which plans include it.
|
|
21
|
+
*/
|
|
22
|
+
export function UpgradeModal({ isOpen, onClose, feature, onUpgrade }: UpgradeModalProps) {
|
|
23
|
+
const { plan: currentPlan, availablePlans } = useWLSubscription();
|
|
24
|
+
|
|
25
|
+
// Sort plans by display order
|
|
26
|
+
const sortedPlans = [...availablePlans].sort((a, b) => a.display_order - b.display_order);
|
|
27
|
+
|
|
28
|
+
// Find the minimum plan that includes the requested feature
|
|
29
|
+
const minimumPlanForFeature = feature
|
|
30
|
+
? sortedPlans.find((p) => p.features.length === 0 || p.features.includes(feature))
|
|
31
|
+
: null;
|
|
32
|
+
|
|
33
|
+
const handleUpgrade = (planSlug: string) => {
|
|
34
|
+
if (onUpgrade) {
|
|
35
|
+
onUpgrade(planSlug);
|
|
36
|
+
}
|
|
37
|
+
// In the future, this would redirect to Stripe Checkout
|
|
38
|
+
// For now, just close the modal
|
|
39
|
+
onClose();
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
return (
|
|
43
|
+
<Dialog open={isOpen} onOpenChange={(open) => !open && onClose()}>
|
|
44
|
+
<DialogContent className="max-w-3xl">
|
|
45
|
+
<DialogHeader>
|
|
46
|
+
<DialogTitle className="flex items-center gap-2">
|
|
47
|
+
<Sparkles className="h-5 w-5 text-primary" />
|
|
48
|
+
Upgrade Your Plan
|
|
49
|
+
</DialogTitle>
|
|
50
|
+
<DialogDescription>
|
|
51
|
+
{feature
|
|
52
|
+
? `Unlock ${getFeatureDisplayName(feature)} and more with an upgraded plan.`
|
|
53
|
+
: "Get access to more features and higher limits."}
|
|
54
|
+
</DialogDescription>
|
|
55
|
+
</DialogHeader>
|
|
56
|
+
|
|
57
|
+
<div className="grid gap-4 py-4 md:grid-cols-3">
|
|
58
|
+
{sortedPlans.map((plan) => (
|
|
59
|
+
<PlanCard
|
|
60
|
+
key={plan.id}
|
|
61
|
+
plan={plan}
|
|
62
|
+
isCurrentPlan={plan.slug === currentPlan?.slug}
|
|
63
|
+
isRecommended={minimumPlanForFeature?.slug === plan.slug}
|
|
64
|
+
highlightFeature={feature}
|
|
65
|
+
onSelect={() => handleUpgrade(plan.slug)}
|
|
66
|
+
/>
|
|
67
|
+
))}
|
|
68
|
+
</div>
|
|
69
|
+
|
|
70
|
+
{sortedPlans.length === 0 && (
|
|
71
|
+
<p className="py-8 text-center text-muted-foreground">
|
|
72
|
+
No upgrade plans available. Contact support for more information.
|
|
73
|
+
</p>
|
|
74
|
+
)}
|
|
75
|
+
</DialogContent>
|
|
76
|
+
</Dialog>
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// ============================================
|
|
81
|
+
// PLAN CARD
|
|
82
|
+
// ============================================
|
|
83
|
+
|
|
84
|
+
type PlanCardProps = {
|
|
85
|
+
plan: WhiteLabelPlan;
|
|
86
|
+
isCurrentPlan: boolean;
|
|
87
|
+
isRecommended: boolean;
|
|
88
|
+
highlightFeature?: GatedFeature;
|
|
89
|
+
onSelect: () => void;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
function PlanCard({ plan, isCurrentPlan, isRecommended, highlightFeature, onSelect }: PlanCardProps) {
|
|
93
|
+
const priceDisplay = plan.is_free
|
|
94
|
+
? "Free"
|
|
95
|
+
: plan.base_price_cents
|
|
96
|
+
? `${(plan.base_price_cents / 100).toFixed(0)}/${plan.billing_interval === "yearly" ? "yr" : "mo"}`
|
|
97
|
+
: "Contact us";
|
|
98
|
+
|
|
99
|
+
const documentsLimit = plan.limits?.documents_per_month ?? "Unlimited";
|
|
100
|
+
|
|
101
|
+
return (
|
|
102
|
+
<div
|
|
103
|
+
className={`relative flex flex-col rounded-lg border p-4 ${
|
|
104
|
+
isRecommended ? "border-primary ring-2 ring-primary" : "border-border"
|
|
105
|
+
} ${isCurrentPlan ? "bg-muted/50" : ""}`}
|
|
106
|
+
>
|
|
107
|
+
{isRecommended && (
|
|
108
|
+
<div className="absolute -top-3 left-1/2 -translate-x-1/2 rounded-full bg-primary px-3 py-1 font-medium text-primary-foreground text-xs">
|
|
109
|
+
Recommended
|
|
110
|
+
</div>
|
|
111
|
+
)}
|
|
112
|
+
|
|
113
|
+
<div className="mb-2 flex items-center gap-2">
|
|
114
|
+
<PlanIcon slug={plan.slug} />
|
|
115
|
+
<h3 className="font-semibold">{plan.name}</h3>
|
|
116
|
+
</div>
|
|
117
|
+
|
|
118
|
+
<div className="mb-1 font-bold text-2xl">
|
|
119
|
+
{plan.is_free ? "" : "€"}
|
|
120
|
+
{priceDisplay}
|
|
121
|
+
</div>
|
|
122
|
+
|
|
123
|
+
<p className="mb-4 text-muted-foreground text-sm">{documentsLimit} documents/month</p>
|
|
124
|
+
|
|
125
|
+
<ul className="mb-4 flex-1 space-y-2">
|
|
126
|
+
{getPlanFeatures(plan.slug).map((featureText) => (
|
|
127
|
+
<li
|
|
128
|
+
key={featureText}
|
|
129
|
+
className={`flex items-center gap-2 text-sm ${
|
|
130
|
+
highlightFeature && featureText.toLowerCase().includes(highlightFeature.replace("_", " "))
|
|
131
|
+
? "font-medium text-primary"
|
|
132
|
+
: ""
|
|
133
|
+
}`}
|
|
134
|
+
>
|
|
135
|
+
<Check className="h-4 w-4 flex-shrink-0 text-green-500" />
|
|
136
|
+
{featureText}
|
|
137
|
+
</li>
|
|
138
|
+
))}
|
|
139
|
+
</ul>
|
|
140
|
+
|
|
141
|
+
<Button
|
|
142
|
+
variant={isCurrentPlan ? "outline" : isRecommended ? "default" : "secondary"}
|
|
143
|
+
className="w-full"
|
|
144
|
+
disabled={isCurrentPlan}
|
|
145
|
+
onClick={onSelect}
|
|
146
|
+
>
|
|
147
|
+
{isCurrentPlan ? "Current Plan" : `Upgrade to ${plan.name}`}
|
|
148
|
+
</Button>
|
|
149
|
+
</div>
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// ============================================
|
|
154
|
+
// HELPERS
|
|
155
|
+
// ============================================
|
|
156
|
+
|
|
157
|
+
function PlanIcon({ slug }: { slug: string }) {
|
|
158
|
+
switch (slug) {
|
|
159
|
+
case "free":
|
|
160
|
+
return <Zap className="h-5 w-5 text-muted-foreground" />;
|
|
161
|
+
case "starter":
|
|
162
|
+
return <Sparkles className="h-5 w-5 text-blue-500" />;
|
|
163
|
+
case "advanced":
|
|
164
|
+
return <Crown className="h-5 w-5 text-amber-500" />;
|
|
165
|
+
default:
|
|
166
|
+
return <Zap className="h-5 w-5 text-muted-foreground" />;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function getFeatureDisplayName(feature: GatedFeature): string {
|
|
171
|
+
const names: Record<GatedFeature, string> = {
|
|
172
|
+
furs: "FURS Fiscalization",
|
|
173
|
+
eslog: "eSlog Export",
|
|
174
|
+
recurring: "Recurring Invoices",
|
|
175
|
+
email_sending: "Email Sending",
|
|
176
|
+
custom_templates: "Custom Templates",
|
|
177
|
+
api_access: "API Access",
|
|
178
|
+
webhooks: "Webhooks",
|
|
179
|
+
priority_support: "Priority Support",
|
|
180
|
+
};
|
|
181
|
+
return names[feature] || feature;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function getPlanFeatures(slug: string): string[] {
|
|
185
|
+
switch (slug) {
|
|
186
|
+
case "free":
|
|
187
|
+
return ["Basic invoicing", "Estimates & quotes", "Customer management", "PDF export"];
|
|
188
|
+
case "starter":
|
|
189
|
+
return [
|
|
190
|
+
"Everything in Free",
|
|
191
|
+
"FURS fiscalization",
|
|
192
|
+
"eSlog export",
|
|
193
|
+
"Recurring invoices",
|
|
194
|
+
"Email sending",
|
|
195
|
+
"100 docs/month",
|
|
196
|
+
];
|
|
197
|
+
case "advanced":
|
|
198
|
+
return [
|
|
199
|
+
"Everything in Starter",
|
|
200
|
+
"Custom templates",
|
|
201
|
+
"API access",
|
|
202
|
+
"Webhooks",
|
|
203
|
+
"Priority support",
|
|
204
|
+
"2,500 docs/month",
|
|
205
|
+
];
|
|
206
|
+
default:
|
|
207
|
+
return [];
|
|
208
|
+
}
|
|
209
|
+
}
|
package/src/frontend.tsx
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file is the entry point for the React app, it sets up the root
|
|
3
|
+
* element and renders the App component to the DOM.
|
|
4
|
+
*
|
|
5
|
+
* It is included in `src/index.html`.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { StrictMode } from "react";
|
|
9
|
+
import { createRoot } from "react-dom/client";
|
|
10
|
+
|
|
11
|
+
import { App } from "./app";
|
|
12
|
+
|
|
13
|
+
const elem = document.getElementById("root")!;
|
|
14
|
+
const app = (
|
|
15
|
+
<StrictMode>
|
|
16
|
+
<App />
|
|
17
|
+
</StrictMode>
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
if (import.meta.hot) {
|
|
21
|
+
// With hot module reloading, `import.meta.hot.data` is persisted.
|
|
22
|
+
import.meta.hot.data.root ??= createRoot(elem);
|
|
23
|
+
const root = import.meta.hot.data.root;
|
|
24
|
+
root.render(app);
|
|
25
|
+
} else {
|
|
26
|
+
// The hot module reloading API is not available in production.
|
|
27
|
+
createRoot(elem).render(app);
|
|
28
|
+
}
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
import { execSync } from "node:child_process";
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
const SCHEMAS_DIR = "./src/generated/schemas";
|
|
6
|
+
const GENERATED_DIR = "./generated";
|
|
7
|
+
|
|
8
|
+
type SchemaGroups = {
|
|
9
|
+
[key: string]: string[];
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
// Helper to get group name from schema name
|
|
13
|
+
const getGroupName = (schemaName: string): string => {
|
|
14
|
+
// Remove _Body and extract the resource name (e.g., Customer from createCustomer)
|
|
15
|
+
return schemaName.replace(/(create|patch|update|delete)([A-Z][a-zA-Z]+)_Body/, "$2").toLowerCase();
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// Helper to format operation name
|
|
19
|
+
const formatOperationName = (operationType: string, resourceName: string): string => {
|
|
20
|
+
// Convert to proper case format (e.g., createCustomer)
|
|
21
|
+
return `${operationType}${resourceName}`;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
async function main() {
|
|
25
|
+
// Ensure directories exist
|
|
26
|
+
await fs.mkdir(SCHEMAS_DIR, { recursive: true });
|
|
27
|
+
await fs.mkdir(GENERATED_DIR, { recursive: true });
|
|
28
|
+
|
|
29
|
+
// Generate initial schemas with openapi-zod-client
|
|
30
|
+
try {
|
|
31
|
+
const openApiPath = path.resolve(__dirname, "../../../apps/api/openapi.json");
|
|
32
|
+
execSync(
|
|
33
|
+
`bunx openapi-zod-client ${openApiPath} ` +
|
|
34
|
+
"--output " +
|
|
35
|
+
GENERATED_DIR +
|
|
36
|
+
"/schemas.ts " +
|
|
37
|
+
"--export-schemas " +
|
|
38
|
+
"--group-strategy none " +
|
|
39
|
+
"--with-docs",
|
|
40
|
+
{ stdio: "inherit" },
|
|
41
|
+
);
|
|
42
|
+
} catch (_error) {
|
|
43
|
+
console.error("Failed to generate schemas.");
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Read and parse the generated file
|
|
48
|
+
let content = await fs.readFile("./generated/schemas.ts", "utf-8");
|
|
49
|
+
|
|
50
|
+
// Fix common generation issues
|
|
51
|
+
content = content.replace(/\.prefault\(/g, ".default(");
|
|
52
|
+
|
|
53
|
+
// Fix Zod v3 record syntax - z.record(z.string()) -> z.record(z.string(), z.any())
|
|
54
|
+
content = content.replace(/z\.record\(z\.string\(\)\)/g, "z.record(z.string(), z.any())");
|
|
55
|
+
|
|
56
|
+
// Remove .default({}) from metadata fields to keep them truly optional
|
|
57
|
+
// When using .optional().default({}), Zod infers the type as required since it always has a value after parsing
|
|
58
|
+
// We want metadata to be optional in the form type to match SDK types
|
|
59
|
+
// Handle both single-line and multi-line patterns (with optional whitespace/newlines between)
|
|
60
|
+
content = content.replace(/\.optional\(\)\s*\.default\(\{\}\)/gs, ".optional()");
|
|
61
|
+
|
|
62
|
+
// Keep nullable types as-is (z.union([z.string(), z.null()]) stays nullable)
|
|
63
|
+
// This matches SDK types which also use | null
|
|
64
|
+
|
|
65
|
+
// Replace undefined named schema references with z.any().optional()
|
|
66
|
+
// These are schemas referenced by name (like TaxRules, EntityBankAccount) that aren't defined in this file
|
|
67
|
+
content = content.replace(/: TaxRules,/g, ": z.any().optional(),");
|
|
68
|
+
content = content.replace(/: EuTaxRules,/g, ": z.any().optional(),");
|
|
69
|
+
content = content.replace(/z\.array\(EntityBankAccount\)/g, "z.array(z.any())");
|
|
70
|
+
|
|
71
|
+
// Extract just the schema definitions
|
|
72
|
+
const schemaMatch = content.match(/export const schemas = {([^}]+)}/s);
|
|
73
|
+
if (!schemaMatch) {
|
|
74
|
+
throw new Error("Could not find schema definitions in generated file");
|
|
75
|
+
}
|
|
76
|
+
const schemaSection = schemaMatch[1];
|
|
77
|
+
|
|
78
|
+
// Helper function to find schema dependencies (schemas referenced by other schemas)
|
|
79
|
+
function findSchemaDependencies(schemaName: string, fullContent: string): string[] {
|
|
80
|
+
const dependencies: string[] = [];
|
|
81
|
+
const schemaDefMatch = fullContent.match(new RegExp(`const ${schemaName} = ([^;]+);`, "s"));
|
|
82
|
+
if (schemaDefMatch) {
|
|
83
|
+
// Find references to other const schemas (e.g., CreateFursInvoiceData.optional())
|
|
84
|
+
const constRefs = schemaDefMatch[1].match(/\b([A-Z][a-zA-Z0-9]+)(?:\.optional\(\)|\.nullable\(\))?/g);
|
|
85
|
+
if (constRefs) {
|
|
86
|
+
for (const ref of constRefs) {
|
|
87
|
+
const cleanRef = ref.replace(/\.(optional|nullable)\(\)/, "");
|
|
88
|
+
// Check if it's actually a schema definition in the file
|
|
89
|
+
if (fullContent.includes(`const ${cleanRef} = z.`)) {
|
|
90
|
+
dependencies.push(cleanRef);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return dependencies;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Parse schema names and group them
|
|
99
|
+
const schemasByGroup = schemaSection
|
|
100
|
+
.split(",\n")
|
|
101
|
+
.map((line) => line.trim())
|
|
102
|
+
.filter((line) => line.includes("_Body"))
|
|
103
|
+
.map((line) => line.split(":")[0].trim())
|
|
104
|
+
.reduce<SchemaGroups>((groups, schemaName) => {
|
|
105
|
+
const groupName = getGroupName(schemaName);
|
|
106
|
+
if (!groups[groupName]) {
|
|
107
|
+
groups[groupName] = [];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Add the main schema
|
|
111
|
+
groups[groupName].push(schemaName);
|
|
112
|
+
|
|
113
|
+
// Find and add dependencies
|
|
114
|
+
const deps = findSchemaDependencies(schemaName, content);
|
|
115
|
+
const queue = [...deps];
|
|
116
|
+
const processed = new Set(deps);
|
|
117
|
+
|
|
118
|
+
while (queue.length > 0) {
|
|
119
|
+
const currentDep = queue.shift()!;
|
|
120
|
+
if (!groups[groupName].includes(currentDep)) {
|
|
121
|
+
groups[groupName].push(currentDep);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const nestedDeps = findSchemaDependencies(currentDep, content);
|
|
125
|
+
for (const nested of nestedDeps) {
|
|
126
|
+
if (!processed.has(nested)) {
|
|
127
|
+
processed.add(nested);
|
|
128
|
+
queue.push(nested);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return groups;
|
|
134
|
+
}, {});
|
|
135
|
+
|
|
136
|
+
// Create files for each group
|
|
137
|
+
for (const [groupName, schemaNames] of Object.entries(schemasByGroup)) {
|
|
138
|
+
// Separate dependencies from operation schemas
|
|
139
|
+
const dependencies = schemaNames.filter((name) => !name.includes("_Body")).reverse();
|
|
140
|
+
const operations = schemaNames.filter((name) => name.includes("_Body"));
|
|
141
|
+
|
|
142
|
+
// Process dependencies first, then operations
|
|
143
|
+
const orderedNames = [...dependencies, ...operations];
|
|
144
|
+
|
|
145
|
+
const schemas = orderedNames
|
|
146
|
+
.map((schemaName) => {
|
|
147
|
+
// Check if this is an operation schema or a dependency
|
|
148
|
+
const operationMatch = schemaName.match(/(create|patch|update|delete|register|upload|send|preview|render)/i);
|
|
149
|
+
const isOperationSchema = operationMatch && schemaName.includes("_Body");
|
|
150
|
+
|
|
151
|
+
if (!isOperationSchema) {
|
|
152
|
+
// This is a dependency schema, just extract its definition
|
|
153
|
+
const schemaDefinitionMatch = content.match(new RegExp(`const ${schemaName} = ([^;]+);`, "s"));
|
|
154
|
+
if (!schemaDefinitionMatch) {
|
|
155
|
+
console.warn(`Warning: Could not find dependency schema definition for ${schemaName}`);
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return `
|
|
160
|
+
// Dependency schema for ${groupName}
|
|
161
|
+
const ${schemaName} = ${schemaDefinitionMatch[1]};
|
|
162
|
+
`;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Handle operation schemas
|
|
166
|
+
const resourceMatch = schemaName.match(/[A-Z][a-zA-Z]+_Body/);
|
|
167
|
+
if (!resourceMatch) {
|
|
168
|
+
console.warn(`Warning: Invalid schema name format: ${schemaName}`);
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const operationType = operationMatch[1].toLowerCase();
|
|
173
|
+
const resourceName = resourceMatch[0].replace("_Body", "");
|
|
174
|
+
const operationName = formatOperationName(operationType, resourceName);
|
|
175
|
+
|
|
176
|
+
const schemaDefinitionMatch = content.match(new RegExp(`const ${schemaName} = ([^;]+);`, "s"));
|
|
177
|
+
if (!schemaDefinitionMatch) {
|
|
178
|
+
console.warn(`Warning: Could not find schema definition for ${schemaName}`);
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
let schemaDefinition = schemaDefinitionMatch[1];
|
|
183
|
+
|
|
184
|
+
// Post-process: Add character limits to name and description fields
|
|
185
|
+
// Add max length validation from API schema (only if not already present)
|
|
186
|
+
schemaDefinition = schemaDefinition.replace(
|
|
187
|
+
/name: z\.string\(\)\.min\(1\)\.max\(100\)(?!\.max)/g,
|
|
188
|
+
'name: z.string().min(1).max(100, "Name must not exceed 100 characters")',
|
|
189
|
+
);
|
|
190
|
+
// Add description character limit - handles both union and nullable variants
|
|
191
|
+
schemaDefinition = schemaDefinition.replace(
|
|
192
|
+
/description: z\.union\(\[z\.string\(\)(?!\.max)/g,
|
|
193
|
+
'description: z.union([z.string().max(4000, "Description must not exceed 4000 characters")',
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
// Add min length to country
|
|
197
|
+
schemaDefinition = schemaDefinition.replace(/country: z\.string\(\)(?!\.min)/g, "country: z.string().min(1)");
|
|
198
|
+
|
|
199
|
+
return `
|
|
200
|
+
// Schema for ${operationType} ${resourceName.toLowerCase()} operation
|
|
201
|
+
const ${operationName}SchemaDefinition = ${schemaDefinition};
|
|
202
|
+
|
|
203
|
+
// Type for ${operationType} ${resourceName.toLowerCase()} operation
|
|
204
|
+
export type ${operationName.slice(0, 1).toUpperCase() + operationName.slice(1)}Schema = z.infer<typeof ${operationName}SchemaDefinition>;
|
|
205
|
+
`;
|
|
206
|
+
})
|
|
207
|
+
.filter((schema) => schema !== null) // Remove null entries
|
|
208
|
+
.join("\n");
|
|
209
|
+
|
|
210
|
+
const exports = schemaNames
|
|
211
|
+
.filter((schemaName) => {
|
|
212
|
+
// Only export operation schemas, not dependencies
|
|
213
|
+
const operationMatch = schemaName.match(/(create|patch|update|delete|register|upload|send|preview|render)/i);
|
|
214
|
+
return operationMatch !== null && schemaName.includes("_Body");
|
|
215
|
+
})
|
|
216
|
+
.map((schemaName) => {
|
|
217
|
+
const operationMatch = schemaName.match(/(create|patch|update|delete|register|upload|send|preview|render)/i);
|
|
218
|
+
const resourceMatch = schemaName.match(/[A-Z][a-zA-Z]+_Body/);
|
|
219
|
+
|
|
220
|
+
if (!operationMatch || !resourceMatch) {
|
|
221
|
+
throw new Error(`Invalid schema name format: ${schemaName}`);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const operationType = operationMatch[1].toLowerCase();
|
|
225
|
+
const resourceName = resourceMatch[0].replace("_Body", "");
|
|
226
|
+
const operationName = formatOperationName(operationType, resourceName);
|
|
227
|
+
return `export const ${operationName}Schema = ${operationName}SchemaDefinition;`;
|
|
228
|
+
})
|
|
229
|
+
.join("\n");
|
|
230
|
+
|
|
231
|
+
const schemaContent = `/**
|
|
232
|
+
* This file was automatically generated using 'bun generate-schemas'.
|
|
233
|
+
* Do not edit this file manually. To update, run the generator again.
|
|
234
|
+
* @generated
|
|
235
|
+
*/
|
|
236
|
+
|
|
237
|
+
import { z } from 'zod';
|
|
238
|
+
|
|
239
|
+
// Schemas for ${groupName} endpoints
|
|
240
|
+
${schemas}
|
|
241
|
+
${exports}
|
|
242
|
+
`;
|
|
243
|
+
|
|
244
|
+
await fs.writeFile(path.join(SCHEMAS_DIR, `${groupName}.ts`), schemaContent);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Create index file
|
|
248
|
+
const indexContent = `/**
|
|
249
|
+
* This file was automatically generated using 'bun generate-schemas'.
|
|
250
|
+
* Do not edit this file manually. To update, run the generator again.
|
|
251
|
+
* @generated
|
|
252
|
+
*/
|
|
253
|
+
|
|
254
|
+
${Object.keys(schemasByGroup)
|
|
255
|
+
.map((groupName) => `export * from './${groupName}';`)
|
|
256
|
+
.join("\n")}
|
|
257
|
+
`;
|
|
258
|
+
|
|
259
|
+
await fs.writeFile(path.join(SCHEMAS_DIR, "index.ts"), indexContent);
|
|
260
|
+
|
|
261
|
+
// Clean up temporary files
|
|
262
|
+
await fs.unlink(`${GENERATED_DIR}/schemas.ts`);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
main().catch(console.error);
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This file was automatically generated using 'bun generate-schemas'.
|
|
3
|
+
* Do not edit this file manually. To update, run the generator again.
|
|
4
|
+
* @generated
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { z } from 'zod';
|
|
8
|
+
|
|
9
|
+
// Schemas for advanceinvoice endpoints
|
|
10
|
+
|
|
11
|
+
// Dependency schema for advanceinvoice
|
|
12
|
+
const LineDiscount = z.object({
|
|
13
|
+
value: z.number().gte(0),
|
|
14
|
+
type: z.enum(["percent", "amount"]).optional().default("percent"),
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
// Schema for create advanceinvoice operation
|
|
19
|
+
const createAdvanceInvoiceSchemaDefinition = z.object({
|
|
20
|
+
is_draft: z.boolean().optional(),
|
|
21
|
+
date: z
|
|
22
|
+
.string()
|
|
23
|
+
.regex(/^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2}(\.\d{3})?Z?)?$/)
|
|
24
|
+
.optional(),
|
|
25
|
+
issuer: z
|
|
26
|
+
.object({
|
|
27
|
+
name: z.union([z.string(), z.null()]),
|
|
28
|
+
email: z.union([z.string(), z.null()]),
|
|
29
|
+
address: z.union([z.string(), z.null()]),
|
|
30
|
+
address_2: z.union([z.string(), z.null()]),
|
|
31
|
+
post_code: z.union([z.string(), z.null()]),
|
|
32
|
+
city: z.union([z.string(), z.null()]),
|
|
33
|
+
state: z.union([z.string(), z.null()]),
|
|
34
|
+
country: z.union([z.string(), z.null()]),
|
|
35
|
+
country_code: z.union([z.string(), z.null()]),
|
|
36
|
+
tax_number: z.union([z.string(), z.null()]),
|
|
37
|
+
bank_account: z.union([
|
|
38
|
+
z
|
|
39
|
+
.object({
|
|
40
|
+
type: z
|
|
41
|
+
.enum(["iban", "us_domestic", "uk_domestic", "other"])
|
|
42
|
+
.default("iban"),
|
|
43
|
+
name: z.string(),
|
|
44
|
+
bank_name: z.string(),
|
|
45
|
+
iban: z.string(),
|
|
46
|
+
account_number: z.string(),
|
|
47
|
+
bic: z.string(),
|
|
48
|
+
routing_number: z.string(),
|
|
49
|
+
sort_code: z.string(),
|
|
50
|
+
})
|
|
51
|
+
.partial()
|
|
52
|
+
.passthrough(),
|
|
53
|
+
z.null(),
|
|
54
|
+
]),
|
|
55
|
+
})
|
|
56
|
+
.partial()
|
|
57
|
+
.passthrough()
|
|
58
|
+
.optional(),
|
|
59
|
+
customer_id: z.union([z.string(), z.null()]).optional(),
|
|
60
|
+
customer: z
|
|
61
|
+
.union([
|
|
62
|
+
z
|
|
63
|
+
.object({
|
|
64
|
+
name: z.union([z.string(), z.null()]),
|
|
65
|
+
email: z.union([z.string(), z.null()]),
|
|
66
|
+
address: z.union([z.string(), z.null()]),
|
|
67
|
+
address_2: z.union([z.string(), z.null()]),
|
|
68
|
+
post_code: z.union([z.string(), z.null()]),
|
|
69
|
+
city: z.union([z.string(), z.null()]),
|
|
70
|
+
state: z.union([z.string(), z.null()]),
|
|
71
|
+
country: z.union([z.string(), z.null()]),
|
|
72
|
+
country_code: z.union([z.string(), z.null()]),
|
|
73
|
+
tax_number: z.union([z.string(), z.null()]),
|
|
74
|
+
bank_account: z.union([
|
|
75
|
+
z
|
|
76
|
+
.object({
|
|
77
|
+
type: z
|
|
78
|
+
.enum(["iban", "us_domestic", "uk_domestic", "other"])
|
|
79
|
+
.default("iban"),
|
|
80
|
+
name: z.string(),
|
|
81
|
+
bank_name: z.string(),
|
|
82
|
+
iban: z.string(),
|
|
83
|
+
account_number: z.string(),
|
|
84
|
+
bic: z.string(),
|
|
85
|
+
routing_number: z.string(),
|
|
86
|
+
sort_code: z.string(),
|
|
87
|
+
})
|
|
88
|
+
.partial()
|
|
89
|
+
.passthrough(),
|
|
90
|
+
z.null(),
|
|
91
|
+
]),
|
|
92
|
+
save_customer: z.boolean().default(true),
|
|
93
|
+
})
|
|
94
|
+
.partial()
|
|
95
|
+
.passthrough(),
|
|
96
|
+
z.null(),
|
|
97
|
+
])
|
|
98
|
+
.optional(),
|
|
99
|
+
note: z.union([z.string(), z.null()]).optional(),
|
|
100
|
+
payment_terms: z.union([z.string(), z.null()]).optional(),
|
|
101
|
+
currency_code: z.string().max(3).optional(),
|
|
102
|
+
metadata: z.union([z.record(z.string(), z.any()), z.null()]).optional(),
|
|
103
|
+
date_due: z.union([z.string(), z.null()]).optional(),
|
|
104
|
+
date_service: z.union([z.string(), z.null()]).optional(),
|
|
105
|
+
date_service_to: z.union([z.string(), z.null()]).optional(),
|
|
106
|
+
items: z
|
|
107
|
+
.array(
|
|
108
|
+
z.object({
|
|
109
|
+
name: z.string().min(1),
|
|
110
|
+
description: z.union([z.string().max(4000, "Description must not exceed 4000 characters"), z.null()]).optional(),
|
|
111
|
+
price: z.number().optional(),
|
|
112
|
+
gross_price: z.number().optional(),
|
|
113
|
+
quantity: z.number().gte(-140737488355328).lte(140737488355327),
|
|
114
|
+
unit: z.union([z.string(), z.null()]).optional(),
|
|
115
|
+
taxes: z
|
|
116
|
+
.array(
|
|
117
|
+
z
|
|
118
|
+
.object({
|
|
119
|
+
rate: z.number(),
|
|
120
|
+
tax_id: z.string(),
|
|
121
|
+
classification: z.string(),
|
|
122
|
+
reverse_charge: z.boolean(),
|
|
123
|
+
amount: z.number(),
|
|
124
|
+
})
|
|
125
|
+
.partial()
|
|
126
|
+
)
|
|
127
|
+
.optional(),
|
|
128
|
+
discounts: z.array(LineDiscount).max(5).optional(),
|
|
129
|
+
metadata: z
|
|
130
|
+
.union([
|
|
131
|
+
z.string(),
|
|
132
|
+
z.number(),
|
|
133
|
+
z.boolean(),
|
|
134
|
+
z.null(),
|
|
135
|
+
z.object({}).partial().passthrough(),
|
|
136
|
+
z.array(z.unknown()),
|
|
137
|
+
z.null(),
|
|
138
|
+
])
|
|
139
|
+
.optional(),
|
|
140
|
+
})
|
|
141
|
+
)
|
|
142
|
+
.min(1),
|
|
143
|
+
linked_documents: z.array(z.string().min(1)).optional(),
|
|
144
|
+
payment: z
|
|
145
|
+
.union([
|
|
146
|
+
z.object({
|
|
147
|
+
advance_invoice_id: z.union([z.string(), z.null()]).optional(),
|
|
148
|
+
amount: z.number().gt(0).optional(),
|
|
149
|
+
type: z.string().max(20),
|
|
150
|
+
date: z.string().optional(),
|
|
151
|
+
reference: z.union([z.string(), z.null()]).optional(),
|
|
152
|
+
note: z.union([z.string(), z.null()]).optional(),
|
|
153
|
+
metadata: z.union([z.record(z.string(), z.any()), z.null()]).optional(),
|
|
154
|
+
}),
|
|
155
|
+
z.null(),
|
|
156
|
+
])
|
|
157
|
+
.optional(),
|
|
158
|
+
furs: z
|
|
159
|
+
.union([
|
|
160
|
+
z
|
|
161
|
+
.object({
|
|
162
|
+
business_premise_name: z.string().min(1),
|
|
163
|
+
electronic_device_name: z.string().min(1),
|
|
164
|
+
operator_tax_number: z.string(),
|
|
165
|
+
operator_label: z.string(),
|
|
166
|
+
skip: z.boolean(),
|
|
167
|
+
})
|
|
168
|
+
.partial(),
|
|
169
|
+
z.null(),
|
|
170
|
+
])
|
|
171
|
+
.optional(),
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
// Type for create advanceinvoice operation
|
|
175
|
+
export type CreateAdvanceInvoiceSchema = z.infer<typeof createAdvanceInvoiceSchemaDefinition>;
|
|
176
|
+
|
|
177
|
+
export const createAdvanceInvoiceSchema = createAdvanceInvoiceSchemaDefinition;
|