@voyant-travel/bookings-react 0.119.3
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 +201 -0
- package/README.md +87 -0
- package/dist/admin/booking-contract-dialog.d.ts +22 -0
- package/dist/admin/booking-contract-dialog.d.ts.map +1 -0
- package/dist/admin/booking-contract-dialog.js +161 -0
- package/dist/admin/booking-detail-host.d.ts +103 -0
- package/dist/admin/booking-detail-host.d.ts.map +1 -0
- package/dist/admin/booking-detail-host.js +127 -0
- package/dist/admin/booking-detail-skeleton.d.ts +7 -0
- package/dist/admin/booking-detail-skeleton.d.ts.map +1 -0
- package/dist/admin/booking-detail-skeleton.js +24 -0
- package/dist/admin/booking-documents-table.d.ts +13 -0
- package/dist/admin/booking-documents-table.d.ts.map +1 -0
- package/dist/admin/booking-documents-table.js +259 -0
- package/dist/admin/booking-invoice-sheet.d.ts +18 -0
- package/dist/admin/booking-invoice-sheet.d.ts.map +1 -0
- package/dist/admin/booking-invoice-sheet.js +101 -0
- package/dist/admin/booking-journey-host.d.ts +24 -0
- package/dist/admin/booking-journey-host.d.ts.map +1 -0
- package/dist/admin/booking-journey-host.js +278 -0
- package/dist/admin/bookings-host.d.ts +26 -0
- package/dist/admin/bookings-host.d.ts.map +1 -0
- package/dist/admin/bookings-host.js +18 -0
- package/dist/admin/bookings-list-skeleton.d.ts +10 -0
- package/dist/admin/bookings-list-skeleton.d.ts.map +1 -0
- package/dist/admin/bookings-list-skeleton.js +25 -0
- package/dist/admin/index.d.ts +273 -0
- package/dist/admin/index.d.ts.map +1 -0
- package/dist/admin/index.js +331 -0
- package/dist/admin/journey-billing-duplicate-warning.d.ts +3 -0
- package/dist/admin/journey-billing-duplicate-warning.d.ts.map +1 -0
- package/dist/admin/journey-billing-duplicate-warning.js +26 -0
- package/dist/admin/journey-departure-picker.d.ts +7 -0
- package/dist/admin/journey-departure-picker.d.ts.map +1 -0
- package/dist/admin/journey-departure-picker.js +100 -0
- package/dist/admin/journey-units-picker.d.ts +11 -0
- package/dist/admin/journey-units-picker.d.ts.map +1 -0
- package/dist/admin/journey-units-picker.js +60 -0
- package/dist/admin/journey-voucher-picker.d.ts +3 -0
- package/dist/admin/journey-voucher-picker.d.ts.map +1 -0
- package/dist/admin/journey-voucher-picker.js +71 -0
- package/dist/admin/pages/booking-compose-page.d.ts +9 -0
- package/dist/admin/pages/booking-compose-page.d.ts.map +1 -0
- package/dist/admin/pages/booking-compose-page.js +17 -0
- package/dist/admin/pages/booking-detail-page.d.ts +11 -0
- package/dist/admin/pages/booking-detail-page.d.ts.map +1 -0
- package/dist/admin/pages/booking-detail-page.js +14 -0
- package/dist/admin/pages/booking-journey-page.d.ts +12 -0
- package/dist/admin/pages/booking-journey-page.d.ts.map +1 -0
- package/dist/admin/pages/booking-journey-page.js +26 -0
- package/dist/admin/pages/booking-new-page.d.ts +15 -0
- package/dist/admin/pages/booking-new-page.d.ts.map +1 -0
- package/dist/admin/pages/booking-new-page.js +50 -0
- package/dist/admin/pages/bookings-index-page.d.ts +20 -0
- package/dist/admin/pages/bookings-index-page.d.ts.map +1 -0
- package/dist/admin/pages/bookings-index-page.js +18 -0
- package/dist/admin/person-bookings-widget.d.ts +13 -0
- package/dist/admin/person-bookings-widget.d.ts.map +1 -0
- package/dist/admin/person-bookings-widget.js +48 -0
- package/dist/admin/slots.d.ts +31 -0
- package/dist/admin/slots.d.ts.map +1 -0
- package/dist/admin/slots.js +30 -0
- package/dist/admin/use-booking-action-ledger-events.d.ts +15 -0
- package/dist/admin/use-booking-action-ledger-events.d.ts.map +1 -0
- package/dist/admin/use-booking-action-ledger-events.js +66 -0
- package/dist/client.d.ts +14 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +59 -0
- package/dist/components/booking-activity-timeline.d.ts +32 -0
- package/dist/components/booking-activity-timeline.d.ts.map +1 -0
- package/dist/components/booking-activity-timeline.js +147 -0
- package/dist/components/booking-billing-dialog.d.ts +16 -0
- package/dist/components/booking-billing-dialog.d.ts.map +1 -0
- package/dist/components/booking-billing-dialog.js +315 -0
- package/dist/components/booking-cancellation-dialog.d.ts +18 -0
- package/dist/components/booking-cancellation-dialog.d.ts.map +1 -0
- package/dist/components/booking-cancellation-dialog.js +79 -0
- package/dist/components/booking-combobox.d.ts +13 -0
- package/dist/components/booking-combobox.d.ts.map +1 -0
- package/dist/components/booking-combobox.js +44 -0
- package/dist/components/booking-create-form-utils.d.ts +56 -0
- package/dist/components/booking-create-form-utils.d.ts.map +1 -0
- package/dist/components/booking-create-form-utils.js +216 -0
- package/dist/components/booking-create-page.d.ts +14 -0
- package/dist/components/booking-create-page.d.ts.map +1 -0
- package/dist/components/booking-create-page.js +11 -0
- package/dist/components/booking-create-preview-card.d.ts +26 -0
- package/dist/components/booking-create-preview-card.d.ts.map +1 -0
- package/dist/components/booking-create-preview-card.js +107 -0
- package/dist/components/booking-create-product-extras-picker.d.ts +18 -0
- package/dist/components/booking-create-product-extras-picker.d.ts.map +1 -0
- package/dist/components/booking-create-product-extras-picker.js +82 -0
- package/dist/components/booking-create-sheet.d.ts +34 -0
- package/dist/components/booking-create-sheet.d.ts.map +1 -0
- package/dist/components/booking-create-sheet.js +811 -0
- package/dist/components/booking-create-utils.d.ts +66 -0
- package/dist/components/booking-create-utils.d.ts.map +1 -0
- package/dist/components/booking-create-utils.js +185 -0
- package/dist/components/booking-detail-page.d.ts +126 -0
- package/dist/components/booking-detail-page.d.ts.map +1 -0
- package/dist/components/booking-detail-page.js +264 -0
- package/dist/components/booking-dialog.d.ts +28 -0
- package/dist/components/booking-dialog.d.ts.map +1 -0
- package/dist/components/booking-dialog.js +130 -0
- package/dist/components/booking-document-dialog.d.ts +8 -0
- package/dist/components/booking-document-dialog.d.ts.map +1 -0
- package/dist/components/booking-document-dialog.js +83 -0
- package/dist/components/booking-document-list.d.ts +5 -0
- package/dist/components/booking-document-list.d.ts.map +1 -0
- package/dist/components/booking-document-list.js +43 -0
- package/dist/components/booking-group-link-dialog.d.ts +10 -0
- package/dist/components/booking-group-link-dialog.d.ts.map +1 -0
- package/dist/components/booking-group-link-dialog.js +79 -0
- package/dist/components/booking-group-section.d.ts +27 -0
- package/dist/components/booking-group-section.d.ts.map +1 -0
- package/dist/components/booking-group-section.js +51 -0
- package/dist/components/booking-guarantee-dialog.d.ts +10 -0
- package/dist/components/booking-guarantee-dialog.d.ts.map +1 -0
- package/dist/components/booking-guarantee-dialog.js +123 -0
- package/dist/components/booking-guarantee-list.d.ts +5 -0
- package/dist/components/booking-guarantee-list.d.ts.map +1 -0
- package/dist/components/booking-guarantee-list.js +86 -0
- package/dist/components/booking-item-dialog.d.ts +10 -0
- package/dist/components/booking-item-dialog.d.ts.map +1 -0
- package/dist/components/booking-item-dialog.js +155 -0
- package/dist/components/booking-item-list.d.ts +12 -0
- package/dist/components/booking-item-list.d.ts.map +1 -0
- package/dist/components/booking-item-list.js +191 -0
- package/dist/components/booking-item-travelers.d.ts +6 -0
- package/dist/components/booking-item-travelers.d.ts.map +1 -0
- package/dist/components/booking-item-travelers.js +57 -0
- package/dist/components/booking-list-filters.d.ts +43 -0
- package/dist/components/booking-list-filters.d.ts.map +1 -0
- package/dist/components/booking-list-filters.js +192 -0
- package/dist/components/booking-list.d.ts +50 -0
- package/dist/components/booking-list.d.ts.map +1 -0
- package/dist/components/booking-list.js +352 -0
- package/dist/components/booking-note-dialog.d.ts +16 -0
- package/dist/components/booking-note-dialog.d.ts.map +1 -0
- package/dist/components/booking-note-dialog.js +41 -0
- package/dist/components/booking-notes.d.ts +5 -0
- package/dist/components/booking-notes.d.ts.map +1 -0
- package/dist/components/booking-notes.js +45 -0
- package/dist/components/booking-payment-reconciliation-banner.d.ts +5 -0
- package/dist/components/booking-payment-reconciliation-banner.d.ts.map +1 -0
- package/dist/components/booking-payment-reconciliation-banner.js +91 -0
- package/dist/components/booking-payment-schedule-dialog.d.ts +10 -0
- package/dist/components/booking-payment-schedule-dialog.d.ts.map +1 -0
- package/dist/components/booking-payment-schedule-dialog.js +117 -0
- package/dist/components/booking-payment-schedule-list.d.ts +10 -0
- package/dist/components/booking-payment-schedule-list.d.ts.map +1 -0
- package/dist/components/booking-payment-schedule-list.js +217 -0
- package/dist/components/booking-payments-summary.d.ts +83 -0
- package/dist/components/booking-payments-summary.d.ts.map +1 -0
- package/dist/components/booking-payments-summary.js +176 -0
- package/dist/components/booking-quick-view-sheet.d.ts +14 -0
- package/dist/components/booking-quick-view-sheet.d.ts.map +1 -0
- package/dist/components/booking-quick-view-sheet.js +283 -0
- package/dist/components/bookings-page.d.ts +19 -0
- package/dist/components/bookings-page.d.ts.map +1 -0
- package/dist/components/bookings-page.js +9 -0
- package/dist/components/file-dropzone.d.ts +25 -0
- package/dist/components/file-dropzone.d.ts.map +1 -0
- package/dist/components/file-dropzone.js +102 -0
- package/dist/components/icon-action-button.d.ts +18 -0
- package/dist/components/icon-action-button.d.ts.map +1 -0
- package/dist/components/icon-action-button.js +13 -0
- package/dist/components/option-units-stepper-section.d.ts +111 -0
- package/dist/components/option-units-stepper-section.d.ts.map +1 -0
- package/dist/components/option-units-stepper-section.js +276 -0
- package/dist/components/payment-schedule-section.d.ts +91 -0
- package/dist/components/payment-schedule-section.d.ts.map +1 -0
- package/dist/components/payment-schedule-section.js +206 -0
- package/dist/components/person-picker-section.d.ts +71 -0
- package/dist/components/person-picker-section.d.ts.map +1 -0
- package/dist/components/person-picker-section.js +160 -0
- package/dist/components/price-breakdown-section.d.ts +83 -0
- package/dist/components/price-breakdown-section.d.ts.map +1 -0
- package/dist/components/price-breakdown-section.js +278 -0
- package/dist/components/product-picker-section.d.ts +29 -0
- package/dist/components/product-picker-section.d.ts.map +1 -0
- package/dist/components/product-picker-section.js +74 -0
- package/dist/components/shared-room-section.d.ts +40 -0
- package/dist/components/shared-room-section.d.ts.map +1 -0
- package/dist/components/shared-room-section.js +99 -0
- package/dist/components/status-badge.d.ts +24 -0
- package/dist/components/status-badge.d.ts.map +1 -0
- package/dist/components/status-badge.js +65 -0
- package/dist/components/status-change-dialog.d.ts +10 -0
- package/dist/components/status-change-dialog.d.ts.map +1 -0
- package/dist/components/status-change-dialog.js +57 -0
- package/dist/components/supplier-status-dialog.d.ts +10 -0
- package/dist/components/supplier-status-dialog.d.ts.map +1 -0
- package/dist/components/supplier-status-dialog.js +98 -0
- package/dist/components/supplier-status-list.d.ts +5 -0
- package/dist/components/supplier-status-list.d.ts.map +1 -0
- package/dist/components/supplier-status-list.js +115 -0
- package/dist/components/traveler-category-buttons.d.ts +26 -0
- package/dist/components/traveler-category-buttons.d.ts.map +1 -0
- package/dist/components/traveler-category-buttons.js +35 -0
- package/dist/components/traveler-dialog.d.ts +10 -0
- package/dist/components/traveler-dialog.d.ts.map +1 -0
- package/dist/components/traveler-dialog.js +256 -0
- package/dist/components/traveler-list.d.ts +6 -0
- package/dist/components/traveler-list.d.ts.map +1 -0
- package/dist/components/traveler-list.js +295 -0
- package/dist/components/travelers-section-controls.d.ts +52 -0
- package/dist/components/travelers-section-controls.d.ts.map +1 -0
- package/dist/components/travelers-section-controls.js +206 -0
- package/dist/components/travelers-section.d.ts +159 -0
- package/dist/components/travelers-section.d.ts.map +1 -0
- package/dist/components/travelers-section.js +355 -0
- package/dist/components/voucher-picker-section.d.ts +50 -0
- package/dist/components/voucher-picker-section.d.ts.map +1 -0
- package/dist/components/voucher-picker-section.js +79 -0
- package/dist/extras/client.d.ts +14 -0
- package/dist/extras/client.d.ts.map +1 -0
- package/dist/extras/client.js +58 -0
- package/dist/extras/components/extra-catalog-card.d.ts +13 -0
- package/dist/extras/components/extra-catalog-card.d.ts.map +1 -0
- package/dist/extras/components/extra-catalog-card.js +52 -0
- package/dist/extras/components/product-combobox.d.ts +9 -0
- package/dist/extras/components/product-combobox.d.ts.map +1 -0
- package/dist/extras/components/product-combobox.js +46 -0
- package/dist/extras/components/slot-extras-manifest-panel.d.ts +6 -0
- package/dist/extras/components/slot-extras-manifest-panel.d.ts.map +1 -0
- package/dist/extras/components/slot-extras-manifest-panel.js +108 -0
- package/dist/extras/hooks/index.d.ts +5 -0
- package/dist/extras/hooks/index.d.ts.map +1 -0
- package/dist/extras/hooks/index.js +4 -0
- package/dist/extras/hooks/use-product-extra.d.ts +24 -0
- package/dist/extras/hooks/use-product-extra.d.ts.map +1 -0
- package/dist/extras/hooks/use-product-extra.js +12 -0
- package/dist/extras/hooks/use-product-extras.d.ts +30 -0
- package/dist/extras/hooks/use-product-extras.d.ts.map +1 -0
- package/dist/extras/hooks/use-product-extras.js +12 -0
- package/dist/extras/hooks/use-slot-extra-manifest-mutation.d.ts +48 -0
- package/dist/extras/hooks/use-slot-extra-manifest-mutation.d.ts.map +1 -0
- package/dist/extras/hooks/use-slot-extra-manifest-mutation.js +26 -0
- package/dist/extras/hooks/use-slot-extra-manifest.d.ts +68 -0
- package/dist/extras/hooks/use-slot-extra-manifest.d.ts.map +1 -0
- package/dist/extras/hooks/use-slot-extra-manifest.js +11 -0
- package/dist/extras/i18n/en.d.ts +52 -0
- package/dist/extras/i18n/en.d.ts.map +1 -0
- package/dist/extras/i18n/en.js +51 -0
- package/dist/extras/i18n/index.d.ts +5 -0
- package/dist/extras/i18n/index.d.ts.map +1 -0
- package/dist/extras/i18n/index.js +3 -0
- package/dist/extras/i18n/messages.d.ts +37 -0
- package/dist/extras/i18n/messages.d.ts.map +1 -0
- package/dist/extras/i18n/messages.js +1 -0
- package/dist/extras/i18n/provider.d.ts +126 -0
- package/dist/extras/i18n/provider.d.ts.map +1 -0
- package/dist/extras/i18n/provider.js +44 -0
- package/dist/extras/i18n/ro.d.ts +52 -0
- package/dist/extras/i18n/ro.d.ts.map +1 -0
- package/dist/extras/i18n/ro.js +51 -0
- package/dist/extras/index.d.ts +7 -0
- package/dist/extras/index.d.ts.map +1 -0
- package/dist/extras/index.js +6 -0
- package/dist/extras/provider.d.ts +2 -0
- package/dist/extras/provider.d.ts.map +1 -0
- package/dist/extras/provider.js +1 -0
- package/dist/extras/query-keys.d.ts +16 -0
- package/dist/extras/query-keys.d.ts.map +1 -0
- package/dist/extras/query-keys.js +8 -0
- package/dist/extras/query-options.d.ts +455 -0
- package/dist/extras/query-options.d.ts.map +1 -0
- package/dist/extras/query-options.js +44 -0
- package/dist/extras/schemas.d.ts +416 -0
- package/dist/extras/schemas.d.ts.map +1 -0
- package/dist/extras/schemas.js +89 -0
- package/dist/extras/ui.d.ts +4 -0
- package/dist/extras/ui.d.ts.map +1 -0
- package/dist/extras/ui.js +3 -0
- package/dist/extras.d.ts +10 -0
- package/dist/extras.d.ts.map +1 -0
- package/dist/extras.js +9 -0
- package/dist/hooks/index.d.ts +36 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +35 -0
- package/dist/hooks/use-booking-action-ledger.d.ts +63 -0
- package/dist/hooks/use-booking-action-ledger.d.ts.map +1 -0
- package/dist/hooks/use-booking-action-ledger.js +34 -0
- package/dist/hooks/use-booking-activity.d.ts +17 -0
- package/dist/hooks/use-booking-activity.d.ts.map +1 -0
- package/dist/hooks/use-booking-activity.js +12 -0
- package/dist/hooks/use-booking-cancel-mutation.d.ts +69 -0
- package/dist/hooks/use-booking-cancel-mutation.d.ts.map +1 -0
- package/dist/hooks/use-booking-cancel-mutation.js +24 -0
- package/dist/hooks/use-booking-contract-generation.d.ts +31 -0
- package/dist/hooks/use-booking-contract-generation.d.ts.map +1 -0
- package/dist/hooks/use-booking-contract-generation.js +36 -0
- package/dist/hooks/use-booking-convert-mutation.d.ts +81 -0
- package/dist/hooks/use-booking-convert-mutation.d.ts.map +1 -0
- package/dist/hooks/use-booking-convert-mutation.js +24 -0
- package/dist/hooks/use-booking-create-mutation.d.ts +337 -0
- package/dist/hooks/use-booking-create-mutation.d.ts.map +1 -0
- package/dist/hooks/use-booking-create-mutation.js +43 -0
- package/dist/hooks/use-booking-documents.d.ts +41 -0
- package/dist/hooks/use-booking-documents.d.ts.map +1 -0
- package/dist/hooks/use-booking-documents.js +46 -0
- package/dist/hooks/use-booking-dual-create-mutation.d.ts +338 -0
- package/dist/hooks/use-booking-dual-create-mutation.d.ts.map +1 -0
- package/dist/hooks/use-booking-dual-create-mutation.js +45 -0
- package/dist/hooks/use-booking-group-for-booking.d.ts +24 -0
- package/dist/hooks/use-booking-group-for-booking.d.ts.map +1 -0
- package/dist/hooks/use-booking-group-for-booking.js +12 -0
- package/dist/hooks/use-booking-group-member-mutation.d.ts +27 -0
- package/dist/hooks/use-booking-group-member-mutation.d.ts.map +1 -0
- package/dist/hooks/use-booking-group-member-mutation.js +38 -0
- package/dist/hooks/use-booking-group-mutation.d.ts +40 -0
- package/dist/hooks/use-booking-group-mutation.d.ts.map +1 -0
- package/dist/hooks/use-booking-group-mutation.js +32 -0
- package/dist/hooks/use-booking-group.d.ts +85 -0
- package/dist/hooks/use-booking-group.d.ts.map +1 -0
- package/dist/hooks/use-booking-group.js +12 -0
- package/dist/hooks/use-booking-groups.d.ts +21 -0
- package/dist/hooks/use-booking-groups.d.ts.map +1 -0
- package/dist/hooks/use-booking-groups.js +12 -0
- package/dist/hooks/use-booking-item-mutation.d.ts +101 -0
- package/dist/hooks/use-booking-item-mutation.d.ts.map +1 -0
- package/dist/hooks/use-booking-item-mutation.js +42 -0
- package/dist/hooks/use-booking-item-travelers.d.ts +32 -0
- package/dist/hooks/use-booking-item-travelers.d.ts.map +1 -0
- package/dist/hooks/use-booking-item-travelers.js +48 -0
- package/dist/hooks/use-booking-items.d.ts +36 -0
- package/dist/hooks/use-booking-items.d.ts.map +1 -0
- package/dist/hooks/use-booking-items.js +12 -0
- package/dist/hooks/use-booking-mutation.d.ts +158 -0
- package/dist/hooks/use-booking-mutation.d.ts.map +1 -0
- package/dist/hooks/use-booking-mutation.js +39 -0
- package/dist/hooks/use-booking-note-mutation.d.ts +39 -0
- package/dist/hooks/use-booking-note-mutation.d.ts.map +1 -0
- package/dist/hooks/use-booking-note-mutation.js +44 -0
- package/dist/hooks/use-booking-notes.d.ts +15 -0
- package/dist/hooks/use-booking-notes.d.ts.map +1 -0
- package/dist/hooks/use-booking-notes.js +12 -0
- package/dist/hooks/use-booking-primary-product.d.ts +28 -0
- package/dist/hooks/use-booking-primary-product.d.ts.map +1 -0
- package/dist/hooks/use-booking-primary-product.js +20 -0
- package/dist/hooks/use-booking-status-mutation.d.ts +156 -0
- package/dist/hooks/use-booking-status-mutation.d.ts.map +1 -0
- package/dist/hooks/use-booking-status-mutation.js +54 -0
- package/dist/hooks/use-booking-tax-preview.d.ts +29 -0
- package/dist/hooks/use-booking-tax-preview.d.ts.map +1 -0
- package/dist/hooks/use-booking-tax-preview.js +21 -0
- package/dist/hooks/use-booking.d.ts +67 -0
- package/dist/hooks/use-booking.d.ts.map +1 -0
- package/dist/hooks/use-booking.js +12 -0
- package/dist/hooks/use-bookings.d.ts +71 -0
- package/dist/hooks/use-bookings.d.ts.map +1 -0
- package/dist/hooks/use-bookings.js +12 -0
- package/dist/hooks/use-pricing-preview.d.ts +61 -0
- package/dist/hooks/use-pricing-preview.d.ts.map +1 -0
- package/dist/hooks/use-pricing-preview.js +18 -0
- package/dist/hooks/use-public-booking-session-flow-mutation.d.ts +148 -0
- package/dist/hooks/use-public-booking-session-flow-mutation.d.ts.map +1 -0
- package/dist/hooks/use-public-booking-session-flow-mutation.js +35 -0
- package/dist/hooks/use-public-booking-session-state.d.ts +16 -0
- package/dist/hooks/use-public-booking-session-state.d.ts.map +1 -0
- package/dist/hooks/use-public-booking-session-state.js +12 -0
- package/dist/hooks/use-public-booking-session.d.ts +101 -0
- package/dist/hooks/use-public-booking-session.d.ts.map +1 -0
- package/dist/hooks/use-public-booking-session.js +12 -0
- package/dist/hooks/use-reveal-traveler.d.ts +54 -0
- package/dist/hooks/use-reveal-traveler.d.ts.map +1 -0
- package/dist/hooks/use-reveal-traveler.js +18 -0
- package/dist/hooks/use-sharing-groups.d.ts +41 -0
- package/dist/hooks/use-sharing-groups.d.ts.map +1 -0
- package/dist/hooks/use-sharing-groups.js +20 -0
- package/dist/hooks/use-supplier-status-mutation.d.ts +46 -0
- package/dist/hooks/use-supplier-status-mutation.d.ts.map +1 -0
- package/dist/hooks/use-supplier-status-mutation.js +39 -0
- package/dist/hooks/use-supplier-statuses.d.ts +20 -0
- package/dist/hooks/use-supplier-statuses.d.ts.map +1 -0
- package/dist/hooks/use-supplier-statuses.js +12 -0
- package/dist/hooks/use-traveler-mutation.d.ts +55 -0
- package/dist/hooks/use-traveler-mutation.d.ts.map +1 -0
- package/dist/hooks/use-traveler-mutation.js +42 -0
- package/dist/hooks/use-traveler-with-travel-details-mutation.d.ts +120 -0
- package/dist/hooks/use-traveler-with-travel-details-mutation.d.ts.map +1 -0
- package/dist/hooks/use-traveler-with-travel-details-mutation.js +43 -0
- package/dist/hooks/use-travelers.d.ts +23 -0
- package/dist/hooks/use-travelers.d.ts.map +1 -0
- package/dist/hooks/use-travelers.js +12 -0
- package/dist/i18n/en-base.d.ts +295 -0
- package/dist/i18n/en-base.d.ts.map +1 -0
- package/dist/i18n/en-base.js +294 -0
- package/dist/i18n/en-create-list.d.ts +327 -0
- package/dist/i18n/en-create-list.d.ts.map +1 -0
- package/dist/i18n/en-create-list.js +326 -0
- package/dist/i18n/en-journey.d.ts +229 -0
- package/dist/i18n/en-journey.d.ts.map +1 -0
- package/dist/i18n/en-journey.js +228 -0
- package/dist/i18n/en-operations.d.ts +382 -0
- package/dist/i18n/en-operations.d.ts.map +1 -0
- package/dist/i18n/en-operations.js +381 -0
- package/dist/i18n/en-sections.d.ts +360 -0
- package/dist/i18n/en-sections.d.ts.map +1 -0
- package/dist/i18n/en-sections.js +359 -0
- package/dist/i18n/en.d.ts +1581 -0
- package/dist/i18n/en.d.ts.map +1 -0
- package/dist/i18n/en.js +12 -0
- package/dist/i18n/index.d.ts +5 -0
- package/dist/i18n/index.d.ts.map +1 -0
- package/dist/i18n/index.js +3 -0
- package/dist/i18n/messages-base.d.ts +251 -0
- package/dist/i18n/messages-base.d.ts.map +1 -0
- package/dist/i18n/messages-base.js +1 -0
- package/dist/i18n/messages-create-list.d.ts +310 -0
- package/dist/i18n/messages-create-list.d.ts.map +1 -0
- package/dist/i18n/messages-create-list.js +1 -0
- package/dist/i18n/messages-journey.d.ts +198 -0
- package/dist/i18n/messages-journey.d.ts.map +1 -0
- package/dist/i18n/messages-journey.js +1 -0
- package/dist/i18n/messages-operations.d.ts +362 -0
- package/dist/i18n/messages-operations.d.ts.map +1 -0
- package/dist/i18n/messages-operations.js +1 -0
- package/dist/i18n/messages-sections.d.ts +312 -0
- package/dist/i18n/messages-sections.d.ts.map +1 -0
- package/dist/i18n/messages-sections.js +1 -0
- package/dist/i18n/messages.d.ts +7 -0
- package/dist/i18n/messages.d.ts.map +1 -0
- package/dist/i18n/messages.js +1 -0
- package/dist/i18n/provider.d.ts +3185 -0
- package/dist/i18n/provider.d.ts.map +1 -0
- package/dist/i18n/provider.js +45 -0
- package/dist/i18n/ro-base.d.ts +295 -0
- package/dist/i18n/ro-base.d.ts.map +1 -0
- package/dist/i18n/ro-base.js +294 -0
- package/dist/i18n/ro-create-list.d.ts +327 -0
- package/dist/i18n/ro-create-list.d.ts.map +1 -0
- package/dist/i18n/ro-create-list.js +326 -0
- package/dist/i18n/ro-journey.d.ts +229 -0
- package/dist/i18n/ro-journey.d.ts.map +1 -0
- package/dist/i18n/ro-journey.js +228 -0
- package/dist/i18n/ro-operations.d.ts +382 -0
- package/dist/i18n/ro-operations.d.ts.map +1 -0
- package/dist/i18n/ro-operations.js +381 -0
- package/dist/i18n/ro-sections.d.ts +360 -0
- package/dist/i18n/ro-sections.d.ts.map +1 -0
- package/dist/i18n/ro-sections.js +359 -0
- package/dist/i18n/ro.d.ts +1581 -0
- package/dist/i18n/ro.d.ts.map +1 -0
- package/dist/i18n/ro.js +12 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/journey/components/booking-journey-rules.d.ts +48 -0
- package/dist/journey/components/booking-journey-rules.d.ts.map +1 -0
- package/dist/journey/components/booking-journey-rules.js +235 -0
- package/dist/journey/components/booking-journey.d.ts +3 -0
- package/dist/journey/components/booking-journey.d.ts.map +1 -0
- package/dist/journey/components/booking-journey.js +368 -0
- package/dist/journey/components/configure-step-skeleton.d.ts +8 -0
- package/dist/journey/components/configure-step-skeleton.d.ts.map +1 -0
- package/dist/journey/components/configure-step-skeleton.js +11 -0
- package/dist/journey/components/contract-preview-dialog.d.ts +47 -0
- package/dist/journey/components/contract-preview-dialog.d.ts.map +1 -0
- package/dist/journey/components/contract-preview-dialog.js +124 -0
- package/dist/journey/components/journey-steps/accommodation-step.d.ts +3 -0
- package/dist/journey/components/journey-steps/accommodation-step.d.ts.map +1 -0
- package/dist/journey/components/journey-steps/accommodation-step.js +71 -0
- package/dist/journey/components/journey-steps/addons-step.d.ts +3 -0
- package/dist/journey/components/journey-steps/addons-step.d.ts.map +1 -0
- package/dist/journey/components/journey-steps/addons-step.js +40 -0
- package/dist/journey/components/journey-steps/billing-step.d.ts +8 -0
- package/dist/journey/components/journey-steps/billing-step.d.ts.map +1 -0
- package/dist/journey/components/journey-steps/billing-step.js +78 -0
- package/dist/journey/components/journey-steps/configure-steps.d.ts +28 -0
- package/dist/journey/components/journey-steps/configure-steps.d.ts.map +1 -0
- package/dist/journey/components/journey-steps/configure-steps.js +232 -0
- package/dist/journey/components/journey-steps/documents-step.d.ts +11 -0
- package/dist/journey/components/journey-steps/documents-step.d.ts.map +1 -0
- package/dist/journey/components/journey-steps/documents-step.js +36 -0
- package/dist/journey/components/journey-steps/payment-step.d.ts +29 -0
- package/dist/journey/components/journey-steps/payment-step.d.ts.map +1 -0
- package/dist/journey/components/journey-steps/payment-step.js +225 -0
- package/dist/journey/components/journey-steps/review-step.d.ts +27 -0
- package/dist/journey/components/journey-steps/review-step.d.ts.map +1 -0
- package/dist/journey/components/journey-steps/review-step.js +18 -0
- package/dist/journey/components/journey-steps/shared.d.ts +75 -0
- package/dist/journey/components/journey-steps/shared.d.ts.map +1 -0
- package/dist/journey/components/journey-steps/shared.js +108 -0
- package/dist/journey/components/journey-steps/travelers-step.d.ts +7 -0
- package/dist/journey/components/journey-steps/travelers-step.d.ts.map +1 -0
- package/dist/journey/components/journey-steps/travelers-step.js +201 -0
- package/dist/journey/components/journey-steps.d.ts +21 -0
- package/dist/journey/components/journey-steps.d.ts.map +1 -0
- package/dist/journey/components/journey-steps.js +20 -0
- package/dist/journey/components/side-panel.d.ts +17 -0
- package/dist/journey/components/side-panel.d.ts.map +1 -0
- package/dist/journey/components/side-panel.js +245 -0
- package/dist/journey/components/stacked-journey.d.ts +30 -0
- package/dist/journey/components/stacked-journey.d.ts.map +1 -0
- package/dist/journey/components/stacked-journey.js +50 -0
- package/dist/journey/components/step-header.d.ts +7 -0
- package/dist/journey/components/step-header.d.ts.map +1 -0
- package/dist/journey/components/step-header.js +12 -0
- package/dist/journey/index.d.ts +18 -0
- package/dist/journey/index.d.ts.map +1 -0
- package/dist/journey/index.js +17 -0
- package/dist/journey/lib/draft-state.d.ts +35 -0
- package/dist/journey/lib/draft-state.d.ts.map +1 -0
- package/dist/journey/lib/draft-state.js +57 -0
- package/dist/journey/lib/pax-band-dependencies.d.ts +27 -0
- package/dist/journey/lib/pax-band-dependencies.d.ts.map +1 -0
- package/dist/journey/lib/pax-band-dependencies.js +50 -0
- package/dist/journey/lib/payment-schedule.d.ts +19 -0
- package/dist/journey/lib/payment-schedule.d.ts.map +1 -0
- package/dist/journey/lib/payment-schedule.js +90 -0
- package/dist/journey/types.d.ts +403 -0
- package/dist/journey/types.d.ts.map +1 -0
- package/dist/journey/types.js +19 -0
- package/dist/provider.d.ts +2 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/provider.js +1 -0
- package/dist/query-keys.d.ts +74 -0
- package/dist/query-keys.d.ts.map +1 -0
- package/dist/query-keys.js +26 -0
- package/dist/query-options.d.ts +2534 -0
- package/dist/query-options.d.ts.map +1 -0
- package/dist/query-options.js +233 -0
- package/dist/requirements/client.d.ts +14 -0
- package/dist/requirements/client.d.ts.map +1 -0
- package/dist/requirements/client.js +59 -0
- package/dist/requirements/components/booking-requirements-contact-tab.d.ts +8 -0
- package/dist/requirements/components/booking-requirements-contact-tab.d.ts.map +1 -0
- package/dist/requirements/components/booking-requirements-contact-tab.js +8 -0
- package/dist/requirements/components/booking-requirements-questions-tab.d.ts +14 -0
- package/dist/requirements/components/booking-requirements-questions-tab.d.ts.map +1 -0
- package/dist/requirements/components/booking-requirements-questions-tab.js +17 -0
- package/dist/requirements/constants.d.ts +114 -0
- package/dist/requirements/constants.d.ts.map +1 -0
- package/dist/requirements/constants.js +45 -0
- package/dist/requirements/hooks/index.d.ts +6 -0
- package/dist/requirements/hooks/index.d.ts.map +1 -0
- package/dist/requirements/hooks/index.js +6 -0
- package/dist/requirements/hooks/use-booking-questions.d.ts +24 -0
- package/dist/requirements/hooks/use-booking-questions.d.ts.map +1 -0
- package/dist/requirements/hooks/use-booking-questions.js +9 -0
- package/dist/requirements/hooks/use-contact-requirements.d.ts +22 -0
- package/dist/requirements/hooks/use-contact-requirements.d.ts.map +1 -0
- package/dist/requirements/hooks/use-contact-requirements.js +9 -0
- package/dist/requirements/hooks/use-products.d.ts +16 -0
- package/dist/requirements/hooks/use-products.d.ts.map +1 -0
- package/dist/requirements/hooks/use-products.js +9 -0
- package/dist/requirements/hooks/use-question-options.d.ts +19 -0
- package/dist/requirements/hooks/use-question-options.d.ts.map +1 -0
- package/dist/requirements/hooks/use-question-options.js +9 -0
- package/dist/requirements/hooks/use-transport-requirements.d.ts +30 -0
- package/dist/requirements/hooks/use-transport-requirements.d.ts.map +1 -0
- package/dist/requirements/hooks/use-transport-requirements.js +9 -0
- package/dist/requirements/i18n/en.d.ts +94 -0
- package/dist/requirements/i18n/en.d.ts.map +1 -0
- package/dist/requirements/i18n/en.js +93 -0
- package/dist/requirements/i18n/index.d.ts +5 -0
- package/dist/requirements/i18n/index.d.ts.map +1 -0
- package/dist/requirements/i18n/index.js +3 -0
- package/dist/requirements/i18n/messages.d.ts +59 -0
- package/dist/requirements/i18n/messages.d.ts.map +1 -0
- package/dist/requirements/i18n/messages.js +1 -0
- package/dist/requirements/i18n/provider.d.ts +210 -0
- package/dist/requirements/i18n/provider.d.ts.map +1 -0
- package/dist/requirements/i18n/provider.js +44 -0
- package/dist/requirements/i18n/ro.d.ts +94 -0
- package/dist/requirements/i18n/ro.d.ts.map +1 -0
- package/dist/requirements/i18n/ro.js +93 -0
- package/dist/requirements/index.d.ts +9 -0
- package/dist/requirements/index.d.ts.map +1 -0
- package/dist/requirements/index.js +8 -0
- package/dist/requirements/provider.d.ts +2 -0
- package/dist/requirements/provider.d.ts.map +1 -0
- package/dist/requirements/provider.js +1 -0
- package/dist/requirements/query-keys.d.ts +33 -0
- package/dist/requirements/query-keys.d.ts.map +1 -0
- package/dist/requirements/query-keys.js +13 -0
- package/dist/requirements/query-options.d.ts +371 -0
- package/dist/requirements/query-options.d.ts.map +1 -0
- package/dist/requirements/query-options.js +80 -0
- package/dist/requirements/schemas.d.ts +320 -0
- package/dist/requirements/schemas.d.ts.map +1 -0
- package/dist/requirements/schemas.js +121 -0
- package/dist/requirements/ui.d.ts +4 -0
- package/dist/requirements/ui.d.ts.map +1 -0
- package/dist/requirements/ui.js +3 -0
- package/dist/requirements/utils.d.ts +2 -0
- package/dist/requirements/utils.d.ts.map +1 -0
- package/dist/requirements/utils.js +4 -0
- package/dist/schemas.d.ts +2070 -0
- package/dist/schemas.d.ts.map +1 -0
- package/dist/schemas.js +507 -0
- package/dist/status-presentation.d.ts +34 -0
- package/dist/status-presentation.d.ts.map +1 -0
- package/dist/status-presentation.js +38 -0
- package/dist/ui.d.ts +43 -0
- package/dist/ui.d.ts.map +1 -0
- package/dist/ui.js +42 -0
- package/package.json +256 -0
- package/src/requirements/styles.css +1 -0
- package/src/styles.css +13 -0
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { useOrganization, useOrganizations, usePeople, usePerson, } from "@voyant-travel/relationships-react";
|
|
4
|
+
import { OrganizationForm, PersonForm } from "@voyant-travel/relationships-react/ui";
|
|
5
|
+
import { Button, Label, Sheet, SheetBody, SheetContent, SheetHeader, SheetTitle, } from "@voyant-travel/ui/components";
|
|
6
|
+
import { Combobox, ComboboxCollection, ComboboxContent, ComboboxEmpty, ComboboxInput, ComboboxItem, ComboboxList, } from "@voyant-travel/ui/components/combobox";
|
|
7
|
+
import { Building2, Pencil, User, UserPlus } from "lucide-react";
|
|
8
|
+
import * as React from "react";
|
|
9
|
+
import { useBookingsUiMessagesOrDefault } from "../i18n/provider.js";
|
|
10
|
+
export const emptyNewPerson = {
|
|
11
|
+
firstName: "",
|
|
12
|
+
lastName: "",
|
|
13
|
+
email: "",
|
|
14
|
+
phone: "",
|
|
15
|
+
};
|
|
16
|
+
export const emptyPersonPickerValue = {
|
|
17
|
+
billTo: "person",
|
|
18
|
+
mode: "existing",
|
|
19
|
+
personId: "",
|
|
20
|
+
newPerson: emptyNewPerson,
|
|
21
|
+
organizationId: null,
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Billing target picker for booking create.
|
|
25
|
+
*
|
|
26
|
+
* State is fully controlled. The embedded create sheets use the CRM forms and
|
|
27
|
+
* select the newly-created person or organization after save.
|
|
28
|
+
*/
|
|
29
|
+
export function PersonPickerSection({ value, onChange, enabled = true, showOrganization = true, hideTargetToggle = false, labels, }) {
|
|
30
|
+
const [personSearch, setPersonSearch] = React.useState("");
|
|
31
|
+
const [orgSearch, setOrgSearch] = React.useState("");
|
|
32
|
+
const cachedPeopleRef = React.useRef(new Map());
|
|
33
|
+
const cachedOrgsRef = React.useRef(new Map());
|
|
34
|
+
const [personInputValue, setPersonInputValue] = React.useState("");
|
|
35
|
+
const [orgInputValue, setOrgInputValue] = React.useState("");
|
|
36
|
+
const [personSheetOpen, setPersonSheetOpen] = React.useState(false);
|
|
37
|
+
const [personSheetMode, setPersonSheetMode] = React.useState("create");
|
|
38
|
+
const [orgSheetOpen, setOrgSheetOpen] = React.useState(false);
|
|
39
|
+
const [orgSheetMode, setOrgSheetMode] = React.useState("create");
|
|
40
|
+
const messages = useBookingsUiMessagesOrDefault();
|
|
41
|
+
const merged = { ...messages.personPickerSection.labels, ...labels };
|
|
42
|
+
const billingTarget = value.billTo ?? "person";
|
|
43
|
+
const { data: peopleData } = usePeople({
|
|
44
|
+
search: personSearch || undefined,
|
|
45
|
+
limit: 20,
|
|
46
|
+
enabled: enabled && billingTarget === "person",
|
|
47
|
+
});
|
|
48
|
+
const selectedPersonQuery = usePerson(value.personId || undefined, {
|
|
49
|
+
enabled: enabled && billingTarget === "person" && Boolean(value.personId),
|
|
50
|
+
});
|
|
51
|
+
const people = React.useMemo(() => {
|
|
52
|
+
const map = new Map(cachedPeopleRef.current);
|
|
53
|
+
for (const person of peopleData?.data ?? [])
|
|
54
|
+
map.set(person.id, person);
|
|
55
|
+
if (selectedPersonQuery.data)
|
|
56
|
+
map.set(selectedPersonQuery.data.id, selectedPersonQuery.data);
|
|
57
|
+
cachedPeopleRef.current = map;
|
|
58
|
+
return Array.from(map.values());
|
|
59
|
+
}, [peopleData?.data, selectedPersonQuery.data]);
|
|
60
|
+
const peopleMap = React.useMemo(() => new Map(people.map((person) => [person.id, person])), [people]);
|
|
61
|
+
const { data: orgsData } = useOrganizations({
|
|
62
|
+
search: orgSearch || undefined,
|
|
63
|
+
limit: 20,
|
|
64
|
+
enabled: enabled && showOrganization && billingTarget === "organization",
|
|
65
|
+
});
|
|
66
|
+
const selectedOrgQuery = useOrganization(value.organizationId || undefined, {
|
|
67
|
+
enabled: enabled && billingTarget === "organization" && Boolean(value.organizationId),
|
|
68
|
+
});
|
|
69
|
+
const orgs = React.useMemo(() => {
|
|
70
|
+
const map = new Map(cachedOrgsRef.current);
|
|
71
|
+
for (const org of orgsData?.data ?? [])
|
|
72
|
+
map.set(org.id, org);
|
|
73
|
+
if (selectedOrgQuery.data)
|
|
74
|
+
map.set(selectedOrgQuery.data.id, selectedOrgQuery.data);
|
|
75
|
+
cachedOrgsRef.current = map;
|
|
76
|
+
return Array.from(map.values());
|
|
77
|
+
}, [orgsData?.data, selectedOrgQuery.data]);
|
|
78
|
+
const orgsMap = React.useMemo(() => new Map(orgs.map((org) => [org.id, org])), [orgs]);
|
|
79
|
+
const setPerson = (patch) => onChange({ ...value, ...patch });
|
|
80
|
+
const resolvePersonLabel = React.useCallback((personId) => formatPerson(peopleMap.get(personId) ?? cachedPeopleRef.current.get(personId)), [peopleMap]);
|
|
81
|
+
const resolveOrgLabel = React.useCallback((organizationId) => orgsMap.get(organizationId)?.name ?? cachedOrgsRef.current.get(organizationId)?.name ?? "", [orgsMap]);
|
|
82
|
+
const selectedPersonLabel = value.personId ? resolvePersonLabel(value.personId) : "";
|
|
83
|
+
const selectedOrgLabel = value.organizationId ? resolveOrgLabel(value.organizationId) : "";
|
|
84
|
+
React.useEffect(() => {
|
|
85
|
+
if (selectedPersonLabel)
|
|
86
|
+
setPersonInputValue(selectedPersonLabel);
|
|
87
|
+
}, [selectedPersonLabel]);
|
|
88
|
+
React.useEffect(() => {
|
|
89
|
+
if (selectedOrgLabel)
|
|
90
|
+
setOrgInputValue(selectedOrgLabel);
|
|
91
|
+
}, [selectedOrgLabel]);
|
|
92
|
+
return (_jsxs(_Fragment, { children: [showOrganization && !hideTargetToggle ? (_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: merged.billTo }), _jsxs("div", { className: "grid grid-cols-2 gap-2", children: [_jsxs(Button, { type: "button", variant: billingTarget === "person" ? "default" : "outline", onClick: () => setPerson({ billTo: "person", organizationId: null }), disabled: !enabled, children: [_jsx(User, { className: "mr-2 h-4 w-4" }), merged.billToPerson] }), _jsxs(Button, { type: "button", variant: billingTarget === "organization" ? "default" : "outline", onClick: () => setPerson({ billTo: "organization", personId: "" }), disabled: !enabled, children: [_jsx(Building2, { className: "mr-2 h-4 w-4" }), merged.billToOrganization] })] })] })) : null, billingTarget === "person" ? (_jsxs("div", { className: "flex flex-col gap-2", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs(Label, { children: [merged.person, " ", _jsx("span", { className: "text-destructive", children: "*" })] }), _jsxs("div", { className: "flex items-center gap-1", children: [value.personId ? (_jsxs(Button, { type: "button", variant: "ghost", size: "sm", className: "h-7", onClick: () => {
|
|
93
|
+
setPersonSheetMode("edit");
|
|
94
|
+
setPersonSheetOpen(true);
|
|
95
|
+
}, disabled: !enabled || !selectedPersonQuery.data, children: [_jsx(Pencil, { className: "mr-1 h-3.5 w-3.5" }), merged.editPerson] })) : null, _jsxs(Button, { type: "button", variant: "ghost", size: "sm", className: "h-7", onClick: () => {
|
|
96
|
+
setPersonSheetMode("create");
|
|
97
|
+
setPersonSheetOpen(true);
|
|
98
|
+
}, disabled: !enabled, children: [_jsx(UserPlus, { className: "mr-1 h-3.5 w-3.5" }), merged.createNewPerson] })] })] }), _jsxs(Combobox, { items: people.map((person) => person.id), value: value.personId || null, inputValue: personInputValue, autoHighlight: true, disabled: !enabled, itemToStringLabel: (id) => resolvePersonLabel(id) || id, itemToStringValue: (id) => id, onInputValueChange: (next) => {
|
|
99
|
+
setPersonInputValue(next);
|
|
100
|
+
setPersonSearch(next);
|
|
101
|
+
if (!next)
|
|
102
|
+
setPerson({ personId: "" });
|
|
103
|
+
}, onValueChange: (next) => {
|
|
104
|
+
const personId = next ?? "";
|
|
105
|
+
setPerson({ personId });
|
|
106
|
+
setPersonInputValue(personId ? resolvePersonLabel(personId) : "");
|
|
107
|
+
}, children: [_jsx(ComboboxInput, { placeholder: merged.personSearchPlaceholder, showClear: !!value.personId }), _jsxs(ComboboxContent, { children: [_jsx(ComboboxEmpty, { children: merged.personEmpty }), _jsx(ComboboxList, { children: _jsx(ComboboxCollection, { children: (id) => {
|
|
108
|
+
const person = peopleMap.get(id);
|
|
109
|
+
if (!person)
|
|
110
|
+
return null;
|
|
111
|
+
return (_jsx(ComboboxItem, { value: person.id, children: _jsxs("div", { className: "flex min-w-0 flex-col", children: [_jsx("span", { className: "truncate font-medium", children: formatPersonName(person) }), person.email ? (_jsx("span", { className: "truncate text-xs text-muted-foreground", children: person.email })) : null] }) }, person.id));
|
|
112
|
+
} }) })] })] })] })) : (_jsxs("div", { className: "flex flex-col gap-2", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs(Label, { children: [merged.organization, " ", _jsx("span", { className: "text-destructive", children: "*" })] }), _jsxs("div", { className: "flex items-center gap-1", children: [value.organizationId ? (_jsxs(Button, { type: "button", variant: "ghost", size: "sm", className: "h-7", onClick: () => {
|
|
113
|
+
setOrgSheetMode("edit");
|
|
114
|
+
setOrgSheetOpen(true);
|
|
115
|
+
}, disabled: !enabled || !selectedOrgQuery.data, children: [_jsx(Pencil, { className: "mr-1 h-3.5 w-3.5" }), merged.editOrganization] })) : null, _jsxs(Button, { type: "button", variant: "ghost", size: "sm", className: "h-7", onClick: () => {
|
|
116
|
+
setOrgSheetMode("create");
|
|
117
|
+
setOrgSheetOpen(true);
|
|
118
|
+
}, disabled: !enabled, children: [_jsx(Building2, { className: "mr-1 h-3.5 w-3.5" }), merged.createNewOrganization] })] })] }), _jsxs(Combobox, { items: orgs.map((org) => org.id), value: value.organizationId ?? null, inputValue: orgInputValue, autoHighlight: true, disabled: !enabled, itemToStringLabel: (id) => resolveOrgLabel(id) || id, itemToStringValue: (id) => id, onInputValueChange: (next) => {
|
|
119
|
+
setOrgInputValue(next);
|
|
120
|
+
setOrgSearch(next);
|
|
121
|
+
if (!next)
|
|
122
|
+
setPerson({ organizationId: null });
|
|
123
|
+
}, onValueChange: (next) => {
|
|
124
|
+
const organizationId = next ?? null;
|
|
125
|
+
setPerson({ organizationId });
|
|
126
|
+
setOrgInputValue(organizationId ? resolveOrgLabel(organizationId) : "");
|
|
127
|
+
}, children: [_jsx(ComboboxInput, { placeholder: merged.organizationSearchPlaceholder, showClear: !!value.organizationId }), _jsxs(ComboboxContent, { children: [_jsx(ComboboxEmpty, { children: merged.organizationEmpty }), _jsx(ComboboxList, { children: _jsx(ComboboxCollection, { children: (id) => {
|
|
128
|
+
const org = orgsMap.get(id);
|
|
129
|
+
if (!org)
|
|
130
|
+
return null;
|
|
131
|
+
return (_jsx(ComboboxItem, { value: org.id, children: _jsxs("div", { className: "flex min-w-0 flex-col", children: [_jsx("span", { className: "truncate font-medium", children: org.name }), org.legalName ? (_jsx("span", { className: "truncate text-xs text-muted-foreground", children: org.legalName })) : null] }) }, org.id));
|
|
132
|
+
} }) })] })] })] })), _jsx(Sheet, { open: personSheetOpen, onOpenChange: setPersonSheetOpen, children: _jsxs(SheetContent, { side: "right", size: "lg", children: [_jsx(SheetHeader, { children: _jsx(SheetTitle, { children: personSheetMode === "edit"
|
|
133
|
+
? merged.editPersonSheetTitle
|
|
134
|
+
: merged.createPersonSheetTitle }) }), _jsx(SheetBody, { children: _jsx(PersonForm, { mode: personSheetMode === "edit" && selectedPersonQuery.data
|
|
135
|
+
? { kind: "edit", person: selectedPersonQuery.data }
|
|
136
|
+
: { kind: "create" }, onCancel: () => setPersonSheetOpen(false), onSuccess: (saved) => {
|
|
137
|
+
setPerson({ billTo: "person", personId: saved.id, organizationId: null });
|
|
138
|
+
setPersonInputValue(formatPerson(saved));
|
|
139
|
+
setPersonSheetOpen(false);
|
|
140
|
+
} }) })] }) }), _jsx(Sheet, { open: orgSheetOpen, onOpenChange: setOrgSheetOpen, children: _jsxs(SheetContent, { side: "right", size: "lg", children: [_jsx(SheetHeader, { children: _jsx(SheetTitle, { children: orgSheetMode === "edit"
|
|
141
|
+
? merged.editOrganizationSheetTitle
|
|
142
|
+
: merged.createOrganizationSheetTitle }) }), _jsx(SheetBody, { children: _jsx(OrganizationForm, { mode: orgSheetMode === "edit" && selectedOrgQuery.data
|
|
143
|
+
? { kind: "edit", organization: selectedOrgQuery.data }
|
|
144
|
+
: { kind: "create" }, onCancel: () => setOrgSheetOpen(false), onSuccess: (saved) => {
|
|
145
|
+
setPerson({ billTo: "organization", personId: "", organizationId: saved.id });
|
|
146
|
+
setOrgInputValue(saved.name);
|
|
147
|
+
setOrgSheetOpen(false);
|
|
148
|
+
} }) })] }) })] }));
|
|
149
|
+
}
|
|
150
|
+
function formatPersonName(person) {
|
|
151
|
+
if (!person)
|
|
152
|
+
return "";
|
|
153
|
+
return [person.firstName, person.lastName].filter(Boolean).join(" ").trim();
|
|
154
|
+
}
|
|
155
|
+
function formatPerson(person) {
|
|
156
|
+
if (!person)
|
|
157
|
+
return "";
|
|
158
|
+
const name = formatPersonName(person);
|
|
159
|
+
return person.email ? `${name} · ${person.email}` : name;
|
|
160
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
export interface PriceBreakdownLine {
|
|
2
|
+
unitId: string;
|
|
3
|
+
pricingCategoryId?: string | null;
|
|
4
|
+
label: string;
|
|
5
|
+
quantity: number;
|
|
6
|
+
/** Per-unit price for the matched tier/row. `null` = on-request pricing. */
|
|
7
|
+
unitAmountCents: number | null;
|
|
8
|
+
/** `unitAmountCents * quantity` or null when on-request. */
|
|
9
|
+
totalAmountCents: number | null;
|
|
10
|
+
/**
|
|
11
|
+
* Populated when a non-default tier matched — operator-visible "N × 100 EUR
|
|
12
|
+
* — group rate" kind of hint. Null for the default tier / single-price row.
|
|
13
|
+
*/
|
|
14
|
+
tierLabel: string | null;
|
|
15
|
+
isGroupRate: boolean;
|
|
16
|
+
}
|
|
17
|
+
export interface PriceBreakdownValue {
|
|
18
|
+
catalogAmountCents: number | null;
|
|
19
|
+
confirmedAmountCents: number | null;
|
|
20
|
+
currency: string | null;
|
|
21
|
+
priceOverrideReason: string;
|
|
22
|
+
isManualOverride: boolean;
|
|
23
|
+
requiresReason: boolean;
|
|
24
|
+
lines: PriceBreakdownLine[];
|
|
25
|
+
}
|
|
26
|
+
export interface PriceBreakdownSectionProps {
|
|
27
|
+
productId?: string;
|
|
28
|
+
optionId?: string | null;
|
|
29
|
+
/** Quantity per option_unit id, typically from OptionUnitsStepperSection. */
|
|
30
|
+
unitQuantities: Record<string, number>;
|
|
31
|
+
/** Display labels keyed by option_unit id. */
|
|
32
|
+
unitLabels?: Record<string, string>;
|
|
33
|
+
/** Traveler pricing quantities keyed by option_unit id and pricing_category id. */
|
|
34
|
+
pricingCategoryQuantities?: Record<string, Record<string, number>>;
|
|
35
|
+
/** Display labels keyed by pricing_category id. */
|
|
36
|
+
pricingCategoryLabels?: Record<string, string>;
|
|
37
|
+
/**
|
|
38
|
+
* Force a specific catalog. Defaults to the public catalog the storefront
|
|
39
|
+
* uses — matches what a customer would see.
|
|
40
|
+
*/
|
|
41
|
+
catalogId?: string | null;
|
|
42
|
+
labels?: {
|
|
43
|
+
heading?: string;
|
|
44
|
+
total?: string;
|
|
45
|
+
onRequest?: string;
|
|
46
|
+
groupRate?: string;
|
|
47
|
+
empty?: string;
|
|
48
|
+
noPricing?: string;
|
|
49
|
+
confirmedTotal?: string;
|
|
50
|
+
manualTotal?: string;
|
|
51
|
+
useCatalogTotal?: string;
|
|
52
|
+
overrideReason?: string;
|
|
53
|
+
overrideReasonPlaceholder?: string;
|
|
54
|
+
overrideReasonRequired?: string;
|
|
55
|
+
};
|
|
56
|
+
onChange?: (value: PriceBreakdownValue) => void;
|
|
57
|
+
/**
|
|
58
|
+
* When true, the section drops its bordered card wrapper and the
|
|
59
|
+
* heading label — for embedding inside another card (e.g. the
|
|
60
|
+
* booking-summary panel) where the parent owns the chrome.
|
|
61
|
+
*/
|
|
62
|
+
flat?: boolean;
|
|
63
|
+
}
|
|
64
|
+
interface UnitPriceLookupRow {
|
|
65
|
+
unitId: string;
|
|
66
|
+
pricingCategoryId?: string | null;
|
|
67
|
+
}
|
|
68
|
+
export declare function createUnitPriceLookup<TUnitPrice extends UnitPriceLookupRow>(unitPrices: ReadonlyArray<TUnitPrice>): (unitId: string, pricingCategoryId: string | null) => TUnitPrice | undefined;
|
|
69
|
+
/**
|
|
70
|
+
* Live price-breakdown preview for booking-create flows. Read-only — uses
|
|
71
|
+
* `usePricingPreview` (#237) to fetch the catalog-resolved snapshot the
|
|
72
|
+
* storefront also uses, then computes lines against the operator's current
|
|
73
|
+
* unit quantities so the operator sees the same numbers the customer would.
|
|
74
|
+
*
|
|
75
|
+
* ### Pricing mode handling
|
|
76
|
+
*
|
|
77
|
+
* - `per_unit` — multiply the matched tier's `sellAmountCents` by quantity.
|
|
78
|
+
* - `free` / `included` — render 0.00 without an on-request badge.
|
|
79
|
+
* - `on_request` / anything else — render "On request"; total excludes it.
|
|
80
|
+
*/
|
|
81
|
+
export declare function PriceBreakdownSection({ productId, optionId, unitQuantities, unitLabels, pricingCategoryQuantities, pricingCategoryLabels, catalogId, labels, onChange, flat, }: PriceBreakdownSectionProps): import("react/jsx-runtime").JSX.Element | null;
|
|
82
|
+
export {};
|
|
83
|
+
//# sourceMappingURL=price-breakdown-section.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"price-breakdown-section.d.ts","sourceRoot":"","sources":["../../src/components/price-breakdown-section.tsx"],"names":[],"mappings":"AASA,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAA;IACd,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACjC,KAAK,EAAE,MAAM,CAAA;IACb,QAAQ,EAAE,MAAM,CAAA;IAChB,4EAA4E;IAC5E,eAAe,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,4DAA4D;IAC5D,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B;;;OAGG;IACH,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,WAAW,EAAE,OAAO,CAAA;CACrB;AAED,MAAM,WAAW,mBAAmB;IAClC,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAA;IACjC,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAA;IACnC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,mBAAmB,EAAE,MAAM,CAAA;IAC3B,gBAAgB,EAAE,OAAO,CAAA;IACzB,cAAc,EAAE,OAAO,CAAA;IACvB,KAAK,EAAE,kBAAkB,EAAE,CAAA;CAC5B;AAED,MAAM,WAAW,0BAA0B;IACzC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,6EAA6E;IAC7E,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACtC,8CAA8C;IAC9C,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACnC,mFAAmF;IACnF,yBAAyB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IAClE,mDAAmD;IACnD,qBAAqB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC9C;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,MAAM,CAAC,EAAE;QACP,OAAO,CAAC,EAAE,MAAM,CAAA;QAChB,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,SAAS,CAAC,EAAE,MAAM,CAAA;QAClB,SAAS,CAAC,EAAE,MAAM,CAAA;QAClB,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,SAAS,CAAC,EAAE,MAAM,CAAA;QAClB,cAAc,CAAC,EAAE,MAAM,CAAA;QACvB,WAAW,CAAC,EAAE,MAAM,CAAA;QACpB,eAAe,CAAC,EAAE,MAAM,CAAA;QACxB,cAAc,CAAC,EAAE,MAAM,CAAA;QACvB,yBAAyB,CAAC,EAAE,MAAM,CAAA;QAClC,sBAAsB,CAAC,EAAE,MAAM,CAAA;KAChC,CAAA;IACD,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,mBAAmB,KAAK,IAAI,CAAA;IAC/C;;;;OAIG;IACH,IAAI,CAAC,EAAE,OAAO,CAAA;CACf;AAQD,UAAU,kBAAkB;IAC1B,MAAM,EAAE,MAAM,CAAA;IACd,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAClC;AAED,wBAAgB,qBAAqB,CAAC,UAAU,SAAS,kBAAkB,EACzE,UAAU,EAAE,aAAa,CAAC,UAAU,CAAC,GACpC,CAAC,MAAM,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,GAAG,IAAI,KAAK,UAAU,GAAG,SAAS,CAwB9E;AAoBD;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CAAC,EACpC,SAAS,EACT,QAAQ,EACR,cAAc,EACd,UAAU,EACV,yBAAyB,EACzB,qBAAqB,EACrB,SAAS,EACT,MAAM,EACN,QAAQ,EACR,IAAY,GACb,EAAE,0BAA0B,kDA2U5B"}
|
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useProduct } from "@voyant-travel/inventory-react";
|
|
4
|
+
import { Button, Label, Textarea } from "@voyant-travel/ui/components";
|
|
5
|
+
import { CurrencyInput } from "@voyant-travel/ui/components/currency-input";
|
|
6
|
+
import * as React from "react";
|
|
7
|
+
import { useBookingsUiI18nOrDefault, useBookingsUiMessagesOrDefault } from "../i18n/provider.js";
|
|
8
|
+
import { usePricingPreview } from "../index.js";
|
|
9
|
+
export function createUnitPriceLookup(unitPrices) {
|
|
10
|
+
const defaultUnitPricesByUnit = new Map();
|
|
11
|
+
const unitPricesByUnitAndCategory = new Map();
|
|
12
|
+
for (const unitPrice of unitPrices) {
|
|
13
|
+
if (unitPrice.pricingCategoryId) {
|
|
14
|
+
unitPricesByUnitAndCategory.set(`${unitPrice.unitId}:${unitPrice.pricingCategoryId}`, unitPrice);
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
if (!defaultUnitPricesByUnit.has(unitPrice.unitId)) {
|
|
18
|
+
defaultUnitPricesByUnit.set(unitPrice.unitId, unitPrice);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return (unitId, pricingCategoryId) => {
|
|
22
|
+
if (!pricingCategoryId)
|
|
23
|
+
return defaultUnitPricesByUnit.get(unitId);
|
|
24
|
+
return (unitPricesByUnitAndCategory.get(`${unitId}:${pricingCategoryId}`) ??
|
|
25
|
+
defaultUnitPricesByUnit.get(unitId));
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Picks the tier whose quantity range contains `qty`. Tiers are expected
|
|
30
|
+
* oldest-to-newest, `minQuantity`-ascending. Ties are broken by first-match —
|
|
31
|
+
* the server sorts by sort_order and then min_quantity, so the selection here
|
|
32
|
+
* mirrors the storefront engine.
|
|
33
|
+
*/
|
|
34
|
+
function matchTier(tiers, qty) {
|
|
35
|
+
for (const tier of tiers) {
|
|
36
|
+
if (qty >= tier.minQuantity &&
|
|
37
|
+
(tier.maxQuantity === null || qty <= tier.maxQuantity) // i18n-literal-ok numeric bounds
|
|
38
|
+
) {
|
|
39
|
+
return tier;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Live price-breakdown preview for booking-create flows. Read-only — uses
|
|
46
|
+
* `usePricingPreview` (#237) to fetch the catalog-resolved snapshot the
|
|
47
|
+
* storefront also uses, then computes lines against the operator's current
|
|
48
|
+
* unit quantities so the operator sees the same numbers the customer would.
|
|
49
|
+
*
|
|
50
|
+
* ### Pricing mode handling
|
|
51
|
+
*
|
|
52
|
+
* - `per_unit` — multiply the matched tier's `sellAmountCents` by quantity.
|
|
53
|
+
* - `free` / `included` — render 0.00 without an on-request badge.
|
|
54
|
+
* - `on_request` / anything else — render "On request"; total excludes it.
|
|
55
|
+
*/
|
|
56
|
+
export function PriceBreakdownSection({ productId, optionId, unitQuantities, unitLabels, pricingCategoryQuantities, pricingCategoryLabels, catalogId, labels, onChange, flat = false, }) {
|
|
57
|
+
const { formatCurrency, formatNumber } = useBookingsUiI18nOrDefault();
|
|
58
|
+
const messages = useBookingsUiMessagesOrDefault();
|
|
59
|
+
const merged = { ...messages.priceBreakdownSection.labels, ...labels };
|
|
60
|
+
const preview = usePricingPreview({
|
|
61
|
+
productId: productId ?? "",
|
|
62
|
+
optionId: optionId ?? null,
|
|
63
|
+
catalogId: catalogId ?? null,
|
|
64
|
+
enabled: Boolean(productId),
|
|
65
|
+
});
|
|
66
|
+
const productQuery = useProduct(productId, { enabled: Boolean(productId) });
|
|
67
|
+
const quantitiesKey = React.useMemo(() => JSON.stringify(unitQuantities), [unitQuantities]);
|
|
68
|
+
const [manualAmountCents, setManualAmountCents] = React.useState(null);
|
|
69
|
+
const [overrideReason, setOverrideReason] = React.useState("");
|
|
70
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: #935 reset manual confirmation when the priced selection changes
|
|
71
|
+
React.useEffect(() => {
|
|
72
|
+
setManualAmountCents(null);
|
|
73
|
+
setOverrideReason("");
|
|
74
|
+
}, [productId, optionId, catalogId, quantitiesKey]);
|
|
75
|
+
const snapshot = preview.data?.data;
|
|
76
|
+
const fallbackProduct = productQuery.data;
|
|
77
|
+
const fallbackUnitAmountCents = fallbackProduct?.sellAmountCents ?? null;
|
|
78
|
+
const currency = snapshot?.catalog.currencyCode ?? fallbackProduct?.sellCurrency ?? null;
|
|
79
|
+
const formatAmount = React.useCallback((cents) => currency
|
|
80
|
+
? formatCurrency(cents / 100, currency)
|
|
81
|
+
: formatNumber(cents / 100, {
|
|
82
|
+
minimumFractionDigits: 2,
|
|
83
|
+
maximumFractionDigits: 2,
|
|
84
|
+
}), [currency, formatCurrency, formatNumber]);
|
|
85
|
+
const { lines, total } = React.useMemo(() => {
|
|
86
|
+
const out = [];
|
|
87
|
+
let runningTotal = 0;
|
|
88
|
+
let anyOnRequest = false;
|
|
89
|
+
if (!snapshot) {
|
|
90
|
+
if (fallbackUnitAmountCents === null)
|
|
91
|
+
return { lines: out, total: null };
|
|
92
|
+
for (const [unitId, quantity] of Object.entries(unitQuantities)) {
|
|
93
|
+
if (quantity <= 0)
|
|
94
|
+
continue;
|
|
95
|
+
const lineTotal = fallbackUnitAmountCents * quantity;
|
|
96
|
+
out.push({
|
|
97
|
+
unitId,
|
|
98
|
+
label: unitLabels?.[unitId] ?? fallbackProduct?.name ?? unitId,
|
|
99
|
+
quantity,
|
|
100
|
+
unitAmountCents: fallbackUnitAmountCents,
|
|
101
|
+
totalAmountCents: lineTotal,
|
|
102
|
+
tierLabel: null,
|
|
103
|
+
isGroupRate: false,
|
|
104
|
+
});
|
|
105
|
+
runningTotal += lineTotal;
|
|
106
|
+
}
|
|
107
|
+
return { lines: out, total: runningTotal };
|
|
108
|
+
}
|
|
109
|
+
// Pick the default price rule for the resolved option (snapshot already
|
|
110
|
+
// filters options by the caller's optionId; rules keep isDefault-first
|
|
111
|
+
// ordering from the server).
|
|
112
|
+
const rulesByOption = new Map();
|
|
113
|
+
for (const rule of snapshot.rules) {
|
|
114
|
+
const existing = rulesByOption.get(rule.optionId) ?? [];
|
|
115
|
+
existing.push(rule);
|
|
116
|
+
rulesByOption.set(rule.optionId, existing);
|
|
117
|
+
}
|
|
118
|
+
const findUnitPrice = createUnitPriceLookup(snapshot.unitPrices);
|
|
119
|
+
for (const [unitId, quantity] of Object.entries(unitQuantities)) {
|
|
120
|
+
if (quantity <= 0)
|
|
121
|
+
continue;
|
|
122
|
+
const categoryEntries = Object.entries(pricingCategoryQuantities?.[unitId] ?? {}).filter(([, categoryQuantity]) => categoryQuantity > 0);
|
|
123
|
+
const selections = categoryEntries.length > 0
|
|
124
|
+
? categoryEntries.map(([pricingCategoryId, categoryQuantity]) => ({
|
|
125
|
+
pricingCategoryId,
|
|
126
|
+
quantity: categoryQuantity,
|
|
127
|
+
unitPrice: findUnitPrice(unitId, pricingCategoryId),
|
|
128
|
+
}))
|
|
129
|
+
: [
|
|
130
|
+
{
|
|
131
|
+
pricingCategoryId: null,
|
|
132
|
+
quantity,
|
|
133
|
+
unitPrice: findUnitPrice(unitId, null),
|
|
134
|
+
},
|
|
135
|
+
];
|
|
136
|
+
for (const selection of selections) {
|
|
137
|
+
const up = selection.unitPrice;
|
|
138
|
+
if (!up) {
|
|
139
|
+
// The unit isn't priced in this catalog — show it on-request so the
|
|
140
|
+
// operator knows they need to quote manually.
|
|
141
|
+
out.push({
|
|
142
|
+
unitId,
|
|
143
|
+
pricingCategoryId: selection.pricingCategoryId,
|
|
144
|
+
label: unitLabels?.[unitId] ?? unitId,
|
|
145
|
+
quantity: selection.quantity,
|
|
146
|
+
unitAmountCents: null,
|
|
147
|
+
totalAmountCents: null,
|
|
148
|
+
tierLabel: null,
|
|
149
|
+
isGroupRate: false,
|
|
150
|
+
});
|
|
151
|
+
anyOnRequest = true;
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
const categoryLabel = selection.pricingCategoryId
|
|
155
|
+
? pricingCategoryLabels?.[selection.pricingCategoryId]
|
|
156
|
+
: null;
|
|
157
|
+
const unitLabel = unitLabels?.[unitId] ?? up.unitName ?? unitId;
|
|
158
|
+
const label = categoryLabel ? `${unitLabel} · ${categoryLabel}` : unitLabel;
|
|
159
|
+
if (up.pricingMode === "on_request") {
|
|
160
|
+
out.push({
|
|
161
|
+
unitId,
|
|
162
|
+
pricingCategoryId: selection.pricingCategoryId,
|
|
163
|
+
label,
|
|
164
|
+
quantity: selection.quantity,
|
|
165
|
+
unitAmountCents: null,
|
|
166
|
+
totalAmountCents: null,
|
|
167
|
+
tierLabel: merged.onRequest,
|
|
168
|
+
isGroupRate: false,
|
|
169
|
+
});
|
|
170
|
+
anyOnRequest = true;
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
if (up.pricingMode === "free" || up.pricingMode === "included") {
|
|
174
|
+
out.push({
|
|
175
|
+
unitId,
|
|
176
|
+
pricingCategoryId: selection.pricingCategoryId,
|
|
177
|
+
label,
|
|
178
|
+
quantity: selection.quantity,
|
|
179
|
+
unitAmountCents: 0,
|
|
180
|
+
totalAmountCents: 0,
|
|
181
|
+
tierLabel: null,
|
|
182
|
+
isGroupRate: false,
|
|
183
|
+
});
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
// per_unit (and anything else that falls through to explicit amounts).
|
|
187
|
+
const matchedTier = matchTier(up.tiers, selection.quantity);
|
|
188
|
+
const unitAmount = matchedTier?.sellAmountCents ?? up.sellAmountCents;
|
|
189
|
+
if (unitAmount === null) {
|
|
190
|
+
out.push({
|
|
191
|
+
unitId,
|
|
192
|
+
pricingCategoryId: selection.pricingCategoryId,
|
|
193
|
+
label,
|
|
194
|
+
quantity: selection.quantity,
|
|
195
|
+
unitAmountCents: null,
|
|
196
|
+
totalAmountCents: null,
|
|
197
|
+
tierLabel: merged.onRequest,
|
|
198
|
+
isGroupRate: false,
|
|
199
|
+
});
|
|
200
|
+
anyOnRequest = true;
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
const lineTotal = unitAmount * selection.quantity;
|
|
204
|
+
const isGroupRate = matchedTier !== null && matchedTier.minQuantity > 1;
|
|
205
|
+
out.push({
|
|
206
|
+
unitId,
|
|
207
|
+
pricingCategoryId: selection.pricingCategoryId,
|
|
208
|
+
label,
|
|
209
|
+
quantity: selection.quantity,
|
|
210
|
+
unitAmountCents: unitAmount,
|
|
211
|
+
totalAmountCents: lineTotal,
|
|
212
|
+
tierLabel: isGroupRate ? merged.groupRate : null,
|
|
213
|
+
isGroupRate,
|
|
214
|
+
});
|
|
215
|
+
runningTotal += lineTotal;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return { lines: out, total: anyOnRequest ? null : runningTotal };
|
|
219
|
+
}, [
|
|
220
|
+
snapshot,
|
|
221
|
+
fallbackProduct?.name,
|
|
222
|
+
fallbackUnitAmountCents,
|
|
223
|
+
unitQuantities,
|
|
224
|
+
unitLabels,
|
|
225
|
+
pricingCategoryQuantities,
|
|
226
|
+
pricingCategoryLabels,
|
|
227
|
+
merged.onRequest,
|
|
228
|
+
merged.groupRate,
|
|
229
|
+
]);
|
|
230
|
+
const confirmedAmountCents = manualAmountCents ?? total;
|
|
231
|
+
const isManualOverride = manualAmountCents != null && (total === null || manualAmountCents !== total);
|
|
232
|
+
const requiresReason = isManualOverride && overrideReason.trim().length === 0;
|
|
233
|
+
React.useEffect(() => {
|
|
234
|
+
onChange?.({
|
|
235
|
+
catalogAmountCents: total,
|
|
236
|
+
confirmedAmountCents,
|
|
237
|
+
currency,
|
|
238
|
+
priceOverrideReason: overrideReason,
|
|
239
|
+
isManualOverride,
|
|
240
|
+
requiresReason,
|
|
241
|
+
lines,
|
|
242
|
+
});
|
|
243
|
+
}, [
|
|
244
|
+
confirmedAmountCents,
|
|
245
|
+
currency,
|
|
246
|
+
isManualOverride,
|
|
247
|
+
lines,
|
|
248
|
+
onChange,
|
|
249
|
+
overrideReason,
|
|
250
|
+
requiresReason,
|
|
251
|
+
total,
|
|
252
|
+
]);
|
|
253
|
+
const manualTotalControls = (_jsxs("div", { className: "flex flex-col gap-2 border-t pt-2", children: [_jsxs("div", { className: "grid gap-2 sm:grid-cols-[minmax(0,1fr)_auto] sm:items-end", children: [_jsxs("div", { className: "flex flex-col gap-1", children: [_jsx(Label, { className: "text-xs", children: merged.manualTotal }), _jsx(CurrencyInput, { value: manualAmountCents, onChange: setManualAmountCents, currency: currency ?? undefined,
|
|
254
|
+
// CurrencyInput already renders the currency symbol + code
|
|
255
|
+
// as addons; the placeholder must be the bare number so we
|
|
256
|
+
// don't end up with `€ €790.00 EUR` showing.
|
|
257
|
+
placeholder: total === null
|
|
258
|
+
? merged.onRequest
|
|
259
|
+
: formatNumber(total / 100, {
|
|
260
|
+
minimumFractionDigits: 2,
|
|
261
|
+
maximumFractionDigits: 2,
|
|
262
|
+
}) })] }), manualAmountCents != null ? (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => setManualAmountCents(null), children: merged.useCatalogTotal })) : null] }), isManualOverride ? (_jsxs("div", { className: "flex flex-col gap-1", children: [_jsx(Label, { className: "text-xs", children: merged.overrideReason }), _jsx(Textarea, { value: overrideReason, onChange: (event) => setOverrideReason(event.target.value), placeholder: merged.overrideReasonPlaceholder, rows: 2 }), requiresReason ? (_jsx("p", { className: "text-xs text-destructive", children: merged.overrideReasonRequired })) : null] })) : null] }));
|
|
263
|
+
// Empty states
|
|
264
|
+
if (!productId)
|
|
265
|
+
return null;
|
|
266
|
+
const wrapperClassName = flat
|
|
267
|
+
? "flex flex-col gap-2" // i18n-literal-ok: tailwind utilities
|
|
268
|
+
: "flex flex-col gap-2 rounded-md border p-3"; // i18n-literal-ok: tailwind utilities
|
|
269
|
+
if ((preview.isError || (preview.isSuccess && !snapshot)) && fallbackUnitAmountCents === null) {
|
|
270
|
+
return (_jsxs("div", { className: wrapperClassName, children: [flat ? null : _jsx(Label, { children: merged.heading }), _jsx("p", { className: "text-xs text-muted-foreground", children: merged.noPricing }), manualTotalControls] }));
|
|
271
|
+
}
|
|
272
|
+
if (lines.length === 0) {
|
|
273
|
+
return (_jsxs("div", { className: wrapperClassName, children: [flat ? null : _jsx(Label, { children: merged.heading }), _jsx("p", { className: "text-xs text-muted-foreground", children: merged.empty }), manualTotalControls] }));
|
|
274
|
+
}
|
|
275
|
+
return (_jsxs("div", { className: wrapperClassName, children: [flat ? null : _jsx(Label, { children: merged.heading }), _jsx("div", { className: "flex flex-col gap-1.5", children: lines.map((line) => (_jsxs("div", { className: "flex items-baseline justify-between text-sm", children: [_jsxs("div", { className: "flex items-baseline gap-2", children: [_jsxs("span", { className: "tabular-nums", children: [formatNumber(line.quantity), "x"] }), _jsx("span", { children: line.label }), line.tierLabel ? (_jsxs("span", { className: "text-xs text-muted-foreground", children: ["\u00B7 ", line.tierLabel] })) : null] }), _jsx("div", { className: "tabular-nums", children: line.totalAmountCents === null
|
|
276
|
+
? merged.onRequest
|
|
277
|
+
: formatAmount(line.totalAmountCents) })] }, line.unitId))) }), _jsxs("div", { className: "mt-1 flex items-baseline justify-between border-t pt-2 text-sm font-medium", children: [_jsx("span", { children: merged.total }), _jsx("span", { className: "tabular-nums", children: total === null ? merged.onRequest : formatAmount(total) })] }), _jsxs("div", { className: "flex items-baseline justify-between text-sm font-medium", children: [_jsx("span", { children: merged.confirmedTotal }), _jsx("span", { className: "tabular-nums", children: confirmedAmountCents === null ? merged.onRequest : formatAmount(confirmedAmountCents) })] }), manualTotalControls] }));
|
|
278
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export interface ProductPickerValue {
|
|
2
|
+
productId: string;
|
|
3
|
+
/** `null` means "no specific option" — the product has options but none was picked. */
|
|
4
|
+
optionId: string | null;
|
|
5
|
+
}
|
|
6
|
+
export interface ProductPickerSectionProps {
|
|
7
|
+
value: ProductPickerValue;
|
|
8
|
+
onChange: (value: ProductPickerValue) => void;
|
|
9
|
+
/** When true, skip data fetches (dialog closed / parent gated). */
|
|
10
|
+
enabled?: boolean;
|
|
11
|
+
/** When true, hide the product picker and fix the productId (e.g., launched from a product page). */
|
|
12
|
+
lockProduct?: boolean;
|
|
13
|
+
/** When false, product options are selected downstream as quantities instead of a single global choice. */
|
|
14
|
+
showOptionPicker?: boolean;
|
|
15
|
+
labels?: {
|
|
16
|
+
product?: string;
|
|
17
|
+
productSearchPlaceholder?: string;
|
|
18
|
+
productSelectPlaceholder?: string;
|
|
19
|
+
option?: string;
|
|
20
|
+
optionNone?: string;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Controlled product + option picker. Splits `value` + `onChange` so apps can
|
|
25
|
+
* replace the whole section (e.g., with a typeahead against a custom catalog)
|
|
26
|
+
* without reimplementing the cascade logic, or keep this one and swap labels.
|
|
27
|
+
*/
|
|
28
|
+
export declare function ProductPickerSection({ value, onChange, enabled, lockProduct, showOptionPicker, labels, }: ProductPickerSectionProps): import("react/jsx-runtime").JSX.Element;
|
|
29
|
+
//# sourceMappingURL=product-picker-section.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"product-picker-section.d.ts","sourceRoot":"","sources":["../../src/components/product-picker-section.tsx"],"names":[],"mappings":"AA+BA,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAA;IACjB,uFAAuF;IACvF,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;CACxB;AAED,MAAM,WAAW,yBAAyB;IACxC,KAAK,EAAE,kBAAkB,CAAA;IACzB,QAAQ,EAAE,CAAC,KAAK,EAAE,kBAAkB,KAAK,IAAI,CAAA;IAC7C,mEAAmE;IACnE,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,qGAAqG;IACrG,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,2GAA2G;IAC3G,gBAAgB,CAAC,EAAE,OAAO,CAAA;IAC1B,MAAM,CAAC,EAAE;QACP,OAAO,CAAC,EAAE,MAAM,CAAA;QAChB,wBAAwB,CAAC,EAAE,MAAM,CAAA;QACjC,wBAAwB,CAAC,EAAE,MAAM,CAAA;QACjC,MAAM,CAAC,EAAE,MAAM,CAAA;QACf,UAAU,CAAC,EAAE,MAAM,CAAA;KACpB,CAAA;CACF;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,EACnC,KAAK,EACL,QAAQ,EACR,OAAc,EACd,WAAmB,EACnB,gBAAuB,EACvB,MAAM,GACP,EAAE,yBAAyB,2CAyI3B"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { useProduct, useProductOptions, useProducts, } from "@voyant-travel/inventory-react";
|
|
4
|
+
import { Label, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@voyant-travel/ui/components";
|
|
5
|
+
import { Combobox, ComboboxCollection, ComboboxContent, ComboboxEmpty, ComboboxInput, ComboboxItem, ComboboxList, } from "@voyant-travel/ui/components/combobox";
|
|
6
|
+
import * as React from "react";
|
|
7
|
+
import { useBookingsUiMessagesOrDefault } from "../i18n/provider.js";
|
|
8
|
+
import { productMatchesPickerSearch } from "./booking-create-utils.js";
|
|
9
|
+
const OPTION_NONE = "__none__";
|
|
10
|
+
/**
|
|
11
|
+
* Controlled product + option picker. Splits `value` + `onChange` so apps can
|
|
12
|
+
* replace the whole section (e.g., with a typeahead against a custom catalog)
|
|
13
|
+
* without reimplementing the cascade logic, or keep this one and swap labels.
|
|
14
|
+
*/
|
|
15
|
+
export function ProductPickerSection({ value, onChange, enabled = true, lockProduct = false, showOptionPicker = true, labels, }) {
|
|
16
|
+
const [productSearch, setProductSearch] = React.useState("");
|
|
17
|
+
const cachedProductsRef = React.useRef(new Map());
|
|
18
|
+
const messages = useBookingsUiMessagesOrDefault();
|
|
19
|
+
const merged = { ...messages.productPickerSection.labels, ...labels };
|
|
20
|
+
const { data: productsData } = useProducts({
|
|
21
|
+
search: productSearch || undefined,
|
|
22
|
+
limit: 20,
|
|
23
|
+
enabled: enabled && !lockProduct,
|
|
24
|
+
});
|
|
25
|
+
const selectedProductQuery = useProduct(value.productId || undefined, {
|
|
26
|
+
enabled: enabled && Boolean(value.productId),
|
|
27
|
+
});
|
|
28
|
+
const products = React.useMemo(() => {
|
|
29
|
+
const map = new Map(cachedProductsRef.current);
|
|
30
|
+
for (const product of productsData?.data ?? [])
|
|
31
|
+
map.set(product.id, product);
|
|
32
|
+
if (selectedProductQuery.data)
|
|
33
|
+
map.set(selectedProductQuery.data.id, selectedProductQuery.data);
|
|
34
|
+
cachedProductsRef.current = map;
|
|
35
|
+
return Array.from(map.values());
|
|
36
|
+
}, [productsData?.data, selectedProductQuery.data]);
|
|
37
|
+
const productMap = React.useMemo(() => new Map(products.map((product) => [product.id, product])), [products]);
|
|
38
|
+
const resolveProductLabel = React.useCallback((productId) => productMap.get(productId)?.name ?? cachedProductsRef.current.get(productId)?.name ?? "", [productMap]);
|
|
39
|
+
const selectedProductLabel = value.productId ? resolveProductLabel(value.productId) : "";
|
|
40
|
+
const [productInputValue, setProductInputValue] = React.useState(selectedProductLabel);
|
|
41
|
+
React.useEffect(() => {
|
|
42
|
+
if (selectedProductLabel)
|
|
43
|
+
setProductInputValue(selectedProductLabel);
|
|
44
|
+
}, [selectedProductLabel]);
|
|
45
|
+
const { data: optionsData } = useProductOptions({
|
|
46
|
+
productId: value.productId || undefined,
|
|
47
|
+
limit: 50,
|
|
48
|
+
enabled: enabled && showOptionPicker && Boolean(value.productId),
|
|
49
|
+
});
|
|
50
|
+
const options = optionsData?.data ?? [];
|
|
51
|
+
return (_jsxs(_Fragment, { children: [!lockProduct && (_jsxs("div", { className: "flex flex-col gap-2", children: [_jsxs(Label, { children: [merged.product, " ", _jsx("span", { className: "text-destructive", children: "*" })] }), _jsxs(Combobox, { items: products.map((product) => product.id), value: value.productId || null, inputValue: productInputValue, autoHighlight: true, disabled: !enabled, filter: (id, query) => productMatchesPickerSearch(productMap.get(id), query), itemToStringLabel: (id) => resolveProductLabel(id) || id, itemToStringValue: (id) => id, onInputValueChange: (next) => {
|
|
52
|
+
setProductInputValue(next);
|
|
53
|
+
setProductSearch(next);
|
|
54
|
+
if (!next)
|
|
55
|
+
onChange({ productId: "", optionId: null });
|
|
56
|
+
}, onValueChange: (next) => {
|
|
57
|
+
const productId = next ?? "";
|
|
58
|
+
onChange({ productId, optionId: null });
|
|
59
|
+
setProductInputValue(productId ? resolveProductLabel(productId) : "");
|
|
60
|
+
}, children: [_jsx(ComboboxInput, { placeholder: merged.productSearchPlaceholder, showClear: !!value.productId }), _jsxs(ComboboxContent, { children: [_jsx(ComboboxEmpty, { children: merged.productEmpty }), _jsx(ComboboxList, { children: _jsx(ComboboxCollection, { children: (id) => {
|
|
61
|
+
const product = productMap.get(id);
|
|
62
|
+
if (!product)
|
|
63
|
+
return null;
|
|
64
|
+
return (_jsx(ComboboxItem, { value: product.id, children: _jsxs("div", { className: "flex min-w-0 flex-col", children: [_jsx("span", { className: "truncate font-medium", children: product.name }), _jsxs("span", { className: "truncate text-xs text-muted-foreground", children: [product.sellCurrency, product.sellAmountCents != null
|
|
65
|
+
? ` · ${product.sellAmountCents / 100}`
|
|
66
|
+
: ""] })] }) }, product.id));
|
|
67
|
+
} }) })] })] })] })), showOptionPicker && value.productId && options.length > 0 && (_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: merged.option }), _jsxs(Select, { items: [
|
|
68
|
+
{ label: merged.optionNone, value: OPTION_NONE },
|
|
69
|
+
...options.map((o) => ({ label: o.name, value: o.id })),
|
|
70
|
+
], value: value.optionId ?? OPTION_NONE, onValueChange: (v) => onChange({
|
|
71
|
+
productId: value.productId,
|
|
72
|
+
optionId: v === OPTION_NONE ? null : (v ?? null),
|
|
73
|
+
}), children: [_jsx(SelectTrigger, { className: "w-full", children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: OPTION_NONE, children: merged.optionNone }), options.map((o) => (_jsx(SelectItem, { value: o.id, children: o.name }, o.id)))] })] })] }))] }));
|
|
74
|
+
}
|