@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,811 @@
|
|
|
1
|
+
// agent-quality: file-size exception -- owner: bookings-react; existing UI surface stays co-located until a dedicated split preserves behavior and tests.
|
|
2
|
+
"use client";
|
|
3
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
4
|
+
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
|
5
|
+
import { resolveBookingDraft, resolveBookingExtraLines, travelersToRows, } from "@voyant-travel/bookings/pricing-assignment";
|
|
6
|
+
import { useOptionUnitPriceRules, usePricingCategories, } from "@voyant-travel/commerce-react/pricing";
|
|
7
|
+
import { useAddresses } from "@voyant-travel/identity-react";
|
|
8
|
+
import { useProduct } from "@voyant-travel/inventory-react";
|
|
9
|
+
import { availabilityQueryKeys, getSlotQueryOptions, useSlots, useSlotUnitAvailability, useVoyantAvailabilityContext, } from "@voyant-travel/operations-react/availability";
|
|
10
|
+
import { useOrganization, usePerson } from "@voyant-travel/relationships-react";
|
|
11
|
+
import { Button, Checkbox, Label, Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle, Textarea, } from "@voyant-travel/ui/components";
|
|
12
|
+
import { AsyncCombobox } from "@voyant-travel/ui/components/async-combobox";
|
|
13
|
+
import { Loader2 } from "lucide-react";
|
|
14
|
+
import * as React from "react";
|
|
15
|
+
import { formatMessage, useBookingsUiI18nOrDefault, useBookingsUiMessagesOrDefault, } from "../i18n/provider.js";
|
|
16
|
+
import { useBookingCreateMutation, usePricingPreview, VoyantApiError, } from "../index.js";
|
|
17
|
+
import { formatPayloadResolverMismatchError, generateBookingNumber, hasAnyPaidPayment, inferTravelerPricingCategoryId, isBookingInventoryUnit, isPayloadResolverMismatchBody, mergePricingRoomMetadata, normalizeBookingUnit, paymentScheduleToRows, pricingSnapshotRoomUnits, sameRoomUnits, stripOptionPrefix, stripUnitSuffix, } from "./booking-create-form-utils.js";
|
|
18
|
+
import { BookingPreviewCard } from "./booking-create-preview-card.js";
|
|
19
|
+
import { ProductExtrasPickerSection } from "./booking-create-product-extras-picker.js";
|
|
20
|
+
import { getBookableDepartureSlots, getOverCapacityInventoryAssignments, getSelectedSharedRoomUnitId, getTravelerAssignableStepperUnits, isRealBookingEmail, itemLinesToRows, validateBillingPersonContact, } from "./booking-create-utils.js";
|
|
21
|
+
import { emptyOptionUnitsStepperValue, OptionUnitsStepperSection, } from "./option-units-stepper-section.js";
|
|
22
|
+
import { emptyPaymentScheduleValue, PaymentScheduleSection, } from "./payment-schedule-section.js";
|
|
23
|
+
import { emptyPersonPickerValue, PersonPickerSection, } from "./person-picker-section.js";
|
|
24
|
+
import { ProductPickerSection } from "./product-picker-section.js";
|
|
25
|
+
import { emptySharedRoomValue, SharedRoomSection, } from "./shared-room-section.js";
|
|
26
|
+
import { emptyTravelerListValue, TravelersSection, } from "./travelers-section.js";
|
|
27
|
+
import { emptyVoucherPickerValue, VoucherPickerSection, } from "./voucher-picker-section.js";
|
|
28
|
+
/**
|
|
29
|
+
* Operator booking-create sheet. Composes the booking-create picker
|
|
30
|
+
* sections — product, departure, rooms, person, shared-room, travelers,
|
|
31
|
+
* price breakdown, voucher, payment schedule — and submits via the atomic
|
|
32
|
+
* `POST /v1/bookings/create` endpoint so partial failures can't
|
|
33
|
+
* leave orphan state.
|
|
34
|
+
*
|
|
35
|
+
* Normally consumed via `BookingDialog` which delegates here when no
|
|
36
|
+
* `booking` prop is passed. Apps that need a bespoke flow can install the
|
|
37
|
+
* sections individually and assemble their own sheet instead of forking.
|
|
38
|
+
*/
|
|
39
|
+
export function BookingCreateSheet({ open, onOpenChange, onCreated, defaultProductId, defaultSlotId, }) {
|
|
40
|
+
const messages = useBookingsUiMessagesOrDefault();
|
|
41
|
+
return (_jsx(Sheet, { open: open, onOpenChange: onOpenChange, children: _jsxs(SheetContent, { side: "right", className: "flex w-full flex-col gap-0 overflow-hidden p-0 sm:max-w-[64rem]!", children: [_jsxs(SheetHeader, { className: "border-b px-6 py-4", children: [_jsx(SheetTitle, { children: messages.bookingCreateDialog.title }), _jsx(SheetDescription, { className: "sr-only", children: messages.bookingCreateDialog.title })] }), _jsx("div", { className: "min-h-0 flex-1 overflow-y-auto px-6 py-4", children: _jsx(BookingCreateForm, { enabled: open, defaultProductId: defaultProductId, defaultSlotId: defaultSlotId, onCancel: () => onOpenChange(false), onCreated: (booking) => {
|
|
42
|
+
onOpenChange(false);
|
|
43
|
+
onCreated?.(booking);
|
|
44
|
+
} }) })] }) }));
|
|
45
|
+
}
|
|
46
|
+
export function BookingCreateForm({ onCreated, defaultProductId, defaultSlotId, enabled = true, onCancel, }) {
|
|
47
|
+
const [product, setProduct] = React.useState({
|
|
48
|
+
productId: defaultProductId ?? "",
|
|
49
|
+
optionId: null,
|
|
50
|
+
});
|
|
51
|
+
const [slotId, setSlotId] = React.useState(defaultSlotId ?? null);
|
|
52
|
+
const [rooms, setRooms] = React.useState(emptyOptionUnitsStepperValue);
|
|
53
|
+
const [roomUnits, setRoomUnits] = React.useState([]);
|
|
54
|
+
const [extraLines, setExtraLines] = React.useState([]);
|
|
55
|
+
const [person, setPerson] = React.useState(emptyPersonPickerValue);
|
|
56
|
+
const [sharedRoom, setSharedRoom] = React.useState(emptySharedRoomValue);
|
|
57
|
+
const [travelers, setTravelers] = React.useState(emptyTravelerListValue);
|
|
58
|
+
const [voucher, setVoucher] = React.useState(emptyVoucherPickerValue);
|
|
59
|
+
const [pricing, setPricing] = React.useState(null);
|
|
60
|
+
const [paymentSchedule, setPaymentScheduleState] = React.useState(emptyPaymentScheduleValue);
|
|
61
|
+
// Tracks whether the operator has manually touched the schedule. The
|
|
62
|
+
// "default the due date to departure" effect below uses this to bow
|
|
63
|
+
// out once the operator has typed their own dates / amounts / paid
|
|
64
|
+
// markers — otherwise we'd thrash their input every time the slot's
|
|
65
|
+
// startsAt re-resolves (combobox re-renders, slot query refetch, …).
|
|
66
|
+
const paymentScheduleTouchedRef = React.useRef(false);
|
|
67
|
+
const setPaymentSchedule = React.useCallback((next) => {
|
|
68
|
+
paymentScheduleTouchedRef.current = true;
|
|
69
|
+
setPaymentScheduleState(next);
|
|
70
|
+
}, []);
|
|
71
|
+
/**
|
|
72
|
+
* Mutually-exclusive document toggles. "Proforma" issues a single
|
|
73
|
+
* placeholder invoice; "Invoice + contract" issues a final invoice
|
|
74
|
+
* alongside the customer contract. Either off → no documents.
|
|
75
|
+
*/
|
|
76
|
+
const [generateProforma, setGenerateProformaState] = React.useState(false);
|
|
77
|
+
const [generateInvoiceAndContract, setGenerateInvoiceAndContractState] = React.useState(false);
|
|
78
|
+
const setGenerateProforma = (next) => {
|
|
79
|
+
setGenerateProformaState(next);
|
|
80
|
+
if (next)
|
|
81
|
+
setGenerateInvoiceAndContractState(false);
|
|
82
|
+
};
|
|
83
|
+
const setGenerateInvoiceAndContract = (next) => {
|
|
84
|
+
setGenerateInvoiceAndContractState(next);
|
|
85
|
+
if (next)
|
|
86
|
+
setGenerateProformaState(false);
|
|
87
|
+
};
|
|
88
|
+
const [notes, setNotes] = React.useState("");
|
|
89
|
+
// Only relevant when the derived status is `confirmed`: when off,
|
|
90
|
+
// the status transition carries `suppressNotifications: true` so
|
|
91
|
+
// the auto-dispatch subscriber skips the customer email + document
|
|
92
|
+
// bundle. Defaults on so the operator opts out, not in.
|
|
93
|
+
const [notifyTraveler, setNotifyTraveler] = React.useState(true);
|
|
94
|
+
const [error, setError] = React.useState(null);
|
|
95
|
+
const [payloadMismatchUnitIds, setPayloadMismatchUnitIds] = React.useState([]);
|
|
96
|
+
const { formatDate } = useBookingsUiI18nOrDefault();
|
|
97
|
+
const messages = useBookingsUiMessagesOrDefault();
|
|
98
|
+
const availabilityClient = useVoyantAvailabilityContext();
|
|
99
|
+
const defaultSlotQuery = useQuery({
|
|
100
|
+
...getSlotQueryOptions(availabilityClient, defaultSlotId),
|
|
101
|
+
enabled: enabled && Boolean(defaultSlotId),
|
|
102
|
+
});
|
|
103
|
+
const defaultSlot = defaultSlotQuery.data?.data ?? null;
|
|
104
|
+
const resolvedDefaultProductId = defaultProductId ?? defaultSlot?.productId ?? "";
|
|
105
|
+
React.useEffect(() => {
|
|
106
|
+
if (!enabled) {
|
|
107
|
+
setProduct({
|
|
108
|
+
productId: resolvedDefaultProductId,
|
|
109
|
+
optionId: defaultSlotId ? (defaultSlot?.optionId ?? null) : null,
|
|
110
|
+
});
|
|
111
|
+
setSlotId(defaultSlotId ?? null);
|
|
112
|
+
setRooms(emptyOptionUnitsStepperValue);
|
|
113
|
+
setRoomUnits([]);
|
|
114
|
+
setExtraLines([]);
|
|
115
|
+
setPerson(emptyPersonPickerValue);
|
|
116
|
+
setSharedRoom(emptySharedRoomValue);
|
|
117
|
+
setTravelers(emptyTravelerListValue);
|
|
118
|
+
setVoucher(emptyVoucherPickerValue);
|
|
119
|
+
setPricing(null);
|
|
120
|
+
setPaymentScheduleState(emptyPaymentScheduleValue);
|
|
121
|
+
paymentScheduleTouchedRef.current = false;
|
|
122
|
+
setGenerateProformaState(false);
|
|
123
|
+
setGenerateInvoiceAndContractState(false);
|
|
124
|
+
setNotes("");
|
|
125
|
+
setNotifyTraveler(true);
|
|
126
|
+
setError(null);
|
|
127
|
+
setPayloadMismatchUnitIds([]);
|
|
128
|
+
}
|
|
129
|
+
else if (resolvedDefaultProductId) {
|
|
130
|
+
setProduct((prev) => {
|
|
131
|
+
const optionId = defaultSlotId
|
|
132
|
+
? (defaultSlot?.optionId ?? null)
|
|
133
|
+
: prev.productId === resolvedDefaultProductId
|
|
134
|
+
? prev.optionId
|
|
135
|
+
: null;
|
|
136
|
+
return prev.productId === resolvedDefaultProductId && prev.optionId === optionId
|
|
137
|
+
? prev
|
|
138
|
+
: { productId: resolvedDefaultProductId, optionId };
|
|
139
|
+
});
|
|
140
|
+
if (defaultSlotId)
|
|
141
|
+
setSlotId(defaultSlotId);
|
|
142
|
+
}
|
|
143
|
+
}, [enabled, resolvedDefaultProductId, defaultSlotId, defaultSlot?.optionId]);
|
|
144
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: booking-create intentionally resets transient departure state only when product id changes; option changes are reconciled against the selected departure below.
|
|
145
|
+
React.useEffect(() => {
|
|
146
|
+
setSlotId(defaultSlotId ?? null);
|
|
147
|
+
setRooms(emptyOptionUnitsStepperValue);
|
|
148
|
+
setRoomUnits([]);
|
|
149
|
+
setSharedRoom(emptySharedRoomValue);
|
|
150
|
+
setExtraLines([]);
|
|
151
|
+
setPayloadMismatchUnitIds([]);
|
|
152
|
+
}, [product.productId, defaultSlotId]);
|
|
153
|
+
const [slotsFromIso, setSlotsFromIso] = React.useState(() => new Date().toISOString());
|
|
154
|
+
React.useEffect(() => {
|
|
155
|
+
if (enabled && product.productId)
|
|
156
|
+
setSlotsFromIso(new Date().toISOString());
|
|
157
|
+
}, [enabled, product.productId]);
|
|
158
|
+
const { data: slotsData } = useSlots({
|
|
159
|
+
productId: product.productId || undefined,
|
|
160
|
+
status: "open",
|
|
161
|
+
startsAtFrom: slotsFromIso,
|
|
162
|
+
limit: 100,
|
|
163
|
+
enabled: enabled && Boolean(product.productId),
|
|
164
|
+
});
|
|
165
|
+
const allOpenSlots = React.useMemo(() => {
|
|
166
|
+
return getBookableDepartureSlots(slotsData?.data ?? [], {
|
|
167
|
+
nowIso: slotsFromIso,
|
|
168
|
+
optionId: null,
|
|
169
|
+
});
|
|
170
|
+
}, [slotsData?.data, slotsFromIso]);
|
|
171
|
+
const slots = React.useMemo(() => {
|
|
172
|
+
const optionSlots = getBookableDepartureSlots(slotsData?.data ?? [], {
|
|
173
|
+
nowIso: slotsFromIso,
|
|
174
|
+
optionId: product.optionId,
|
|
175
|
+
});
|
|
176
|
+
return optionSlots.length > 0 ? optionSlots : allOpenSlots;
|
|
177
|
+
}, [slotsData?.data, slotsFromIso, product.optionId, allOpenSlots]);
|
|
178
|
+
const selectedSlot = React.useMemo(() => slots.find((slot) => slot.id === slotId) ?? (defaultSlot?.id === slotId ? defaultSlot : null), [slots, slotId, defaultSlot]);
|
|
179
|
+
// Default the single Full-mode installment's due date to the departure
|
|
180
|
+
// day so a trip starting weeks/months later doesn't surface today's
|
|
181
|
+
// date as the payment due date. Skip once the operator has touched
|
|
182
|
+
// the schedule (mode change, custom amount, alternate date, …) so we
|
|
183
|
+
// never overwrite their edits.
|
|
184
|
+
const departureDateIso = selectedSlot?.startsAt?.slice(0, 10) ?? null;
|
|
185
|
+
React.useEffect(() => {
|
|
186
|
+
if (!departureDateIso || paymentScheduleTouchedRef.current)
|
|
187
|
+
return;
|
|
188
|
+
setPaymentScheduleState((prev) => {
|
|
189
|
+
if (prev.mode !== "full" || prev.installments.length !== 1)
|
|
190
|
+
return prev;
|
|
191
|
+
const installment = prev.installments[0];
|
|
192
|
+
if (!installment || installment.dueDate === departureDateIso)
|
|
193
|
+
return prev;
|
|
194
|
+
return {
|
|
195
|
+
...prev,
|
|
196
|
+
installments: [{ ...installment, dueDate: departureDateIso }],
|
|
197
|
+
};
|
|
198
|
+
});
|
|
199
|
+
}, [departureDateIso]);
|
|
200
|
+
const setSelectedSlot = React.useCallback((nextSlotId) => {
|
|
201
|
+
setPayloadMismatchUnitIds([]);
|
|
202
|
+
const selectedSlot = nextSlotId ? allOpenSlots.find((slot) => slot.id === nextSlotId) : null;
|
|
203
|
+
if (selectedSlot?.optionId && selectedSlot.optionId !== product.optionId) {
|
|
204
|
+
setProduct((prev) => ({ ...prev, optionId: selectedSlot.optionId }));
|
|
205
|
+
}
|
|
206
|
+
setSlotId(nextSlotId);
|
|
207
|
+
}, [allOpenSlots, product.optionId]);
|
|
208
|
+
React.useEffect(() => {
|
|
209
|
+
setRooms(emptyOptionUnitsStepperValue);
|
|
210
|
+
setRoomUnits([]);
|
|
211
|
+
setPayloadMismatchUnitIds([]);
|
|
212
|
+
if (!slotId || !product.optionId)
|
|
213
|
+
return;
|
|
214
|
+
const selectedDeparture = allOpenSlots.find((slot) => slot.id === slotId) ??
|
|
215
|
+
(defaultSlot?.id === slotId ? defaultSlot : null);
|
|
216
|
+
if (selectedDeparture?.optionId && selectedDeparture.optionId !== product.optionId) {
|
|
217
|
+
if (defaultSlotId && selectedDeparture.id === defaultSlotId) {
|
|
218
|
+
setProduct((prev) => ({ ...prev, optionId: selectedDeparture.optionId }));
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
setSlotId(null);
|
|
222
|
+
}
|
|
223
|
+
}, [allOpenSlots, product.optionId, slotId, defaultSlotId, defaultSlot]);
|
|
224
|
+
const formatSlotLabel = React.useCallback((slot) => {
|
|
225
|
+
const date = formatDate(slot.startsAt, {
|
|
226
|
+
year: "numeric",
|
|
227
|
+
month: "short",
|
|
228
|
+
day: "numeric",
|
|
229
|
+
});
|
|
230
|
+
const remaining = !slot.unlimited && typeof slot.remainingPax === "number"
|
|
231
|
+
? ` · ${slot.remainingPax} ${messages.bookingCreateDialog.labels.remainingCapacity}`
|
|
232
|
+
: "";
|
|
233
|
+
return `${date}${remaining}`;
|
|
234
|
+
}, [formatDate, messages]);
|
|
235
|
+
const slotUnitAvailability = useSlotUnitAvailability({
|
|
236
|
+
slotId: slotId ?? undefined,
|
|
237
|
+
enabled: enabled && Boolean(slotId),
|
|
238
|
+
});
|
|
239
|
+
const pricingPreview = usePricingPreview({
|
|
240
|
+
productId: product.productId,
|
|
241
|
+
optionId: product.optionId,
|
|
242
|
+
enabled: enabled && Boolean(product.productId),
|
|
243
|
+
});
|
|
244
|
+
const pricingCategoriesQuery = usePricingCategories({
|
|
245
|
+
active: true,
|
|
246
|
+
limit: 200,
|
|
247
|
+
enabled: enabled && Boolean(product.productId),
|
|
248
|
+
});
|
|
249
|
+
const optionUnitPriceRulesQuery = useOptionUnitPriceRules({
|
|
250
|
+
optionId: product.optionId ?? selectedSlot?.optionId ?? undefined,
|
|
251
|
+
active: true,
|
|
252
|
+
limit: 200,
|
|
253
|
+
enabled: enabled && Boolean(product.productId),
|
|
254
|
+
});
|
|
255
|
+
const handleRoomUnitsChange = React.useCallback((units) => {
|
|
256
|
+
setRoomUnits((prev) => (sameRoomUnits(prev, units) ? prev : units));
|
|
257
|
+
}, []);
|
|
258
|
+
const pricingRoomUnits = React.useMemo(() => pricingSnapshotRoomUnits(pricingPreview.data?.data), [pricingPreview.data]);
|
|
259
|
+
const hasRoomPricingMatrix = pricingRoomUnits.length > 0;
|
|
260
|
+
const bookingUnits = React.useMemo(() => mergePricingRoomMetadata(roomUnits, pricingRoomUnits), [roomUnits, pricingRoomUnits]);
|
|
261
|
+
// Room choices presented to the traveler row are inventory options
|
|
262
|
+
// (e.g. "Standard double", "Junior suite upgrade"). The age-band
|
|
263
|
+
// lives separately on the traveler as a pricing unit.
|
|
264
|
+
const roomUnitOptions = React.useMemo(() => {
|
|
265
|
+
const sourceUnits = bookingUnits.length > 0 ? bookingUnits : (slotUnitAvailability.data?.data ?? []);
|
|
266
|
+
const units = sourceUnits.filter(isBookingInventoryUnit);
|
|
267
|
+
if (units.length === 0)
|
|
268
|
+
return [];
|
|
269
|
+
return units
|
|
270
|
+
.filter((unit) => (rooms.quantities[unit.optionUnitId] ?? 0) > 0)
|
|
271
|
+
.map((unit) => {
|
|
272
|
+
const totalQty = rooms.quantities[unit.optionUnitId] ?? 0;
|
|
273
|
+
const occupancyMax = Math.max(1, unit.occupancyMax ?? 1);
|
|
274
|
+
const seats = totalQty * occupancyMax;
|
|
275
|
+
const assigned = travelers.travelers.filter((traveler) => traveler.inventoryUnitId === unit.optionUnitId).length;
|
|
276
|
+
return {
|
|
277
|
+
unitId: unit.optionUnitId,
|
|
278
|
+
unitName: stripOptionPrefix(unit.unitName),
|
|
279
|
+
remainingCapacity: Math.max(0, seats - assigned),
|
|
280
|
+
};
|
|
281
|
+
});
|
|
282
|
+
}, [bookingUnits, slotUnitAvailability.data, rooms.quantities, travelers.travelers]);
|
|
283
|
+
// Per-option breakdown of all configured units, with the
|
|
284
|
+
// attributes the TravelersSection's dynamic category buttons need
|
|
285
|
+
// (unitCode/min-max/unitType). Mirrors the grouping logic in
|
|
286
|
+
// `roomUnitOptions` but exposes every unit instead of collapsing
|
|
287
|
+
// to one primary.
|
|
288
|
+
const roomGroups = React.useMemo(() => {
|
|
289
|
+
if (bookingUnits.length === 0)
|
|
290
|
+
return [];
|
|
291
|
+
const groups = new Map();
|
|
292
|
+
for (const rawUnit of bookingUnits) {
|
|
293
|
+
const u = normalizeBookingUnit(rawUnit);
|
|
294
|
+
if (!u.optionId)
|
|
295
|
+
continue;
|
|
296
|
+
const groupKey = u.optionId;
|
|
297
|
+
const isInventory = isBookingInventoryUnit(u);
|
|
298
|
+
const isAdultCoded = (u.unitCode ?? "").toUpperCase() === "ADULT";
|
|
299
|
+
const unit = {
|
|
300
|
+
unitId: u.optionUnitId,
|
|
301
|
+
// Strip the "Option name - " prefix the stepper applies when
|
|
302
|
+
// an option has multiple units; the per-unit label is enough
|
|
303
|
+
// for a category button.
|
|
304
|
+
unitName: stripOptionPrefix(u.unitName),
|
|
305
|
+
unitCode: u.unitCode ?? null,
|
|
306
|
+
minAge: u.minAge ?? null,
|
|
307
|
+
maxAge: u.maxAge ?? null,
|
|
308
|
+
unitType: (u.unitType ?? null),
|
|
309
|
+
};
|
|
310
|
+
const existing = groups.get(groupKey);
|
|
311
|
+
if (existing) {
|
|
312
|
+
existing.units.push(unit);
|
|
313
|
+
if (isInventory)
|
|
314
|
+
existing.primaryUnitId = u.optionUnitId;
|
|
315
|
+
else if (isAdultCoded &&
|
|
316
|
+
!existing.units.some((candidate) => candidate.unitType === "room" || candidate.unitType === "vehicle")) {
|
|
317
|
+
existing.primaryUnitId = u.optionUnitId;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
else {
|
|
321
|
+
groups.set(groupKey, {
|
|
322
|
+
optionId: groupKey,
|
|
323
|
+
optionName: stripUnitSuffix(u.unitName),
|
|
324
|
+
primaryUnitId: u.optionUnitId,
|
|
325
|
+
units: [unit],
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
return Array.from(groups.values());
|
|
330
|
+
}, [bookingUnits]);
|
|
331
|
+
const travelerPricingCategories = React.useMemo(() => {
|
|
332
|
+
const snapshot = pricingPreview.data?.data;
|
|
333
|
+
const categoriesById = new Map();
|
|
334
|
+
const bookingUnitIds = new Set(bookingUnits.map((unit) => unit.optionUnitId));
|
|
335
|
+
for (const category of pricingCategoriesQuery.data?.data ?? []) {
|
|
336
|
+
categoriesById.set(category.id, category);
|
|
337
|
+
}
|
|
338
|
+
for (const category of snapshot?.pricingCategories ?? []) {
|
|
339
|
+
categoriesById.set(category.id, category);
|
|
340
|
+
}
|
|
341
|
+
const unitIdsByCategoryId = new Map();
|
|
342
|
+
for (const unitPrice of snapshot?.unitPrices ?? []) {
|
|
343
|
+
if (!unitPrice.pricingCategoryId)
|
|
344
|
+
continue;
|
|
345
|
+
if (bookingUnitIds.size > 0 && !bookingUnitIds.has(unitPrice.unitId))
|
|
346
|
+
continue;
|
|
347
|
+
const existing = unitIdsByCategoryId.get(unitPrice.pricingCategoryId) ?? new Set();
|
|
348
|
+
existing.add(unitPrice.unitId);
|
|
349
|
+
unitIdsByCategoryId.set(unitPrice.pricingCategoryId, existing);
|
|
350
|
+
}
|
|
351
|
+
for (const unitPriceRule of optionUnitPriceRulesQuery.data?.data ?? []) {
|
|
352
|
+
if (!unitPriceRule.pricingCategoryId)
|
|
353
|
+
continue;
|
|
354
|
+
if (bookingUnitIds.size > 0 && !bookingUnitIds.has(unitPriceRule.unitId))
|
|
355
|
+
continue;
|
|
356
|
+
const existing = unitIdsByCategoryId.get(unitPriceRule.pricingCategoryId) ?? new Set();
|
|
357
|
+
existing.add(unitPriceRule.unitId);
|
|
358
|
+
unitIdsByCategoryId.set(unitPriceRule.pricingCategoryId, existing);
|
|
359
|
+
}
|
|
360
|
+
return Array.from(unitIdsByCategoryId.entries())
|
|
361
|
+
.flatMap(([categoryId, unitIds]) => {
|
|
362
|
+
const category = categoriesById.get(categoryId);
|
|
363
|
+
if (!category)
|
|
364
|
+
return [];
|
|
365
|
+
return [
|
|
366
|
+
{
|
|
367
|
+
categoryId,
|
|
368
|
+
name: category.name,
|
|
369
|
+
code: category.code,
|
|
370
|
+
categoryType: category.categoryType,
|
|
371
|
+
minAge: category.minAge,
|
|
372
|
+
maxAge: category.maxAge,
|
|
373
|
+
unitIds: Array.from(unitIds),
|
|
374
|
+
},
|
|
375
|
+
];
|
|
376
|
+
})
|
|
377
|
+
.sort((left, right) => {
|
|
378
|
+
const leftSort = categoriesById.get(left.categoryId)?.sortOrder ?? 0;
|
|
379
|
+
const rightSort = categoriesById.get(right.categoryId)?.sortOrder ?? 0;
|
|
380
|
+
return leftSort - rightSort || left.name.localeCompare(right.name);
|
|
381
|
+
});
|
|
382
|
+
}, [
|
|
383
|
+
pricingPreview.data,
|
|
384
|
+
pricingCategoriesQuery.data?.data,
|
|
385
|
+
optionUnitPriceRulesQuery.data?.data,
|
|
386
|
+
bookingUnits,
|
|
387
|
+
]);
|
|
388
|
+
const travelerPricingCategoryLabels = React.useMemo(() => Object.fromEntries(travelerPricingCategories.map((category) => [category.categoryId, category.name])), [travelerPricingCategories]);
|
|
389
|
+
const travelerPricingCategoryQuantities = React.useMemo(() => {
|
|
390
|
+
const quantities = {};
|
|
391
|
+
if (travelerPricingCategories.length === 0)
|
|
392
|
+
return quantities;
|
|
393
|
+
for (const traveler of travelers.travelers) {
|
|
394
|
+
if (!traveler.inventoryUnitId)
|
|
395
|
+
continue;
|
|
396
|
+
const pricingCategoryId = inferTravelerPricingCategoryId(traveler, travelerPricingCategories);
|
|
397
|
+
if (!pricingCategoryId)
|
|
398
|
+
continue;
|
|
399
|
+
const unitCategoryQuantities = quantities[traveler.inventoryUnitId] ?? {};
|
|
400
|
+
unitCategoryQuantities[pricingCategoryId] =
|
|
401
|
+
(unitCategoryQuantities[pricingCategoryId] ?? 0) + 1;
|
|
402
|
+
quantities[traveler.inventoryUnitId] = unitCategoryQuantities;
|
|
403
|
+
}
|
|
404
|
+
return quantities;
|
|
405
|
+
}, [travelers.travelers, travelerPricingCategories]);
|
|
406
|
+
// Apply the same draft resolver we use at submit so live pricing
|
|
407
|
+
// and persisted item lines cannot drift. Person-priced options
|
|
408
|
+
// (excursions) derive line quantities from the traveler list;
|
|
409
|
+
// accommodation options preserve operator-picked stepper quantities.
|
|
410
|
+
const displayDraft = React.useMemo(() => resolveBookingDraft({
|
|
411
|
+
quantities: rooms.quantities,
|
|
412
|
+
travelers: travelers.travelers,
|
|
413
|
+
units: bookingUnits,
|
|
414
|
+
}), [rooms.quantities, travelers.travelers, bookingUnits]);
|
|
415
|
+
const displayQuantities = displayDraft.quantities;
|
|
416
|
+
const displayExtraLines = React.useMemo(() => resolveBookingExtraLines({
|
|
417
|
+
extraLines,
|
|
418
|
+
travelerCount: travelers.travelers.length,
|
|
419
|
+
}), [extraLines, travelers.travelers.length]);
|
|
420
|
+
// Currency placeholder — used for voucher + payment schedule display.
|
|
421
|
+
// Consumers hooking in real product data should override this by wrapping
|
|
422
|
+
// the component or swapping in their own currency-aware hook.
|
|
423
|
+
const currency = messages.bookingCreateDialog.labels.currency;
|
|
424
|
+
// Source-of-truth currency for the payment-schedule wire payload: prefer
|
|
425
|
+
// the product's own `sellCurrency` (what the server stamps on the new
|
|
426
|
+
// booking via `convertProductToBooking`), fall back to the pricing
|
|
427
|
+
// preview's currency, and only then fall back to the placeholder. The
|
|
428
|
+
// server rejects the create with `invalid_payment_schedules` when any
|
|
429
|
+
// schedule row's currency drifts from the booking's, so this trio has to
|
|
430
|
+
// resolve to the same value the server will pick.
|
|
431
|
+
const bookingProductQuery = useProduct(product.productId || undefined, {
|
|
432
|
+
enabled: enabled && Boolean(product.productId),
|
|
433
|
+
});
|
|
434
|
+
const productSellCurrency = bookingProductQuery.data?.sellCurrency ?? null;
|
|
435
|
+
const pricingCurrency = productSellCurrency ?? pricing?.currency ?? currency;
|
|
436
|
+
const pricingTotalAmountCents = pricing?.confirmedAmountCents ?? undefined;
|
|
437
|
+
const roomUnitLabels = React.useMemo(() => Object.fromEntries(bookingUnits.map((unit) => [unit.optionUnitId, unit.unitName])), [bookingUnits]);
|
|
438
|
+
const createBookingMutation = useBookingCreateMutation();
|
|
439
|
+
const queryClient = useQueryClient();
|
|
440
|
+
// Resolve the billing person/org once at the dialog level so we can
|
|
441
|
+
// snapshot their contact details into the booking row at create time.
|
|
442
|
+
// The booking row's `contact_*` columns are the source of truth for
|
|
443
|
+
// billing on the detail page — the linked CRM record can change (or
|
|
444
|
+
// be deleted) later without retroactively rewriting history.
|
|
445
|
+
const billingPersonRecord = usePerson((person.billTo ?? "person") === "person" ? (person.personId ?? undefined) : undefined, { enabled: (person.billTo ?? "person") === "person" && Boolean(person.personId) }).data;
|
|
446
|
+
const billingOrganizationRecord = useOrganization(person.billTo === "organization" ? (person.organizationId ?? undefined) : undefined, { enabled: person.billTo === "organization" && Boolean(person.organizationId) }).data;
|
|
447
|
+
// Primary address for whichever billing record was picked. Filter by
|
|
448
|
+
// `entityType` + `entityId` to keep the response small; the first
|
|
449
|
+
// address with `isPrimary` wins (the server returns at most one).
|
|
450
|
+
const billingPrimaryAddressKind = (person.billTo ?? "person") === "person" && person.personId
|
|
451
|
+
? "person"
|
|
452
|
+
: person.billTo === "organization" && person.organizationId
|
|
453
|
+
? "organization"
|
|
454
|
+
: null;
|
|
455
|
+
const billingPrimaryAddressEntityId = billingPrimaryAddressKind === "person"
|
|
456
|
+
? (person.personId ?? undefined)
|
|
457
|
+
: billingPrimaryAddressKind === "organization"
|
|
458
|
+
? (person.organizationId ?? undefined)
|
|
459
|
+
: undefined;
|
|
460
|
+
const billingAddressQuery = useAddresses({
|
|
461
|
+
entityType: billingPrimaryAddressKind ?? undefined,
|
|
462
|
+
entityId: billingPrimaryAddressEntityId,
|
|
463
|
+
isPrimary: true,
|
|
464
|
+
limit: 1,
|
|
465
|
+
enabled: Boolean(billingPrimaryAddressKind && billingPrimaryAddressEntityId),
|
|
466
|
+
});
|
|
467
|
+
const billingPrimaryAddress = billingAddressQuery.data?.data?.[0] ?? null;
|
|
468
|
+
const hasSelectedUnits = React.useMemo(() => Object.values(rooms.quantities).some((qty) => qty > 0), [rooms.quantities]);
|
|
469
|
+
const handleSubmit = async () => {
|
|
470
|
+
setError(null);
|
|
471
|
+
setPayloadMismatchUnitIds([]);
|
|
472
|
+
if (!product.productId) {
|
|
473
|
+
setError(messages.bookingCreateDialog.validation.selectProduct);
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
if (!slotId) {
|
|
477
|
+
setError(messages.bookingCreateDialog.validation.selectDeparture);
|
|
478
|
+
return;
|
|
479
|
+
}
|
|
480
|
+
if (!hasSelectedUnits) {
|
|
481
|
+
setError(messages.bookingCreateDialog.validation.selectUnits);
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
let resolvedPersonId = null;
|
|
485
|
+
let resolvedOrganizationId = null;
|
|
486
|
+
if ((person.billTo ?? "person") === "person") {
|
|
487
|
+
if (!person.personId) {
|
|
488
|
+
setError(messages.bookingCreateDialog.validation.selectPerson);
|
|
489
|
+
return;
|
|
490
|
+
}
|
|
491
|
+
resolvedPersonId = person.personId;
|
|
492
|
+
const billingContactValidation = validateBillingPersonContact(billingPersonRecord);
|
|
493
|
+
if (billingContactValidation === "missing-contact") {
|
|
494
|
+
setError(messages.bookingCreateDialog.validation.billingContactRequired);
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
if (billingContactValidation === "invalid-email") {
|
|
498
|
+
setError(messages.bookingCreateDialog.validation.billingEmailInvalid);
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
else {
|
|
503
|
+
if (!person.organizationId) {
|
|
504
|
+
setError(messages.bookingCreateDialog.validation.selectOrganization);
|
|
505
|
+
return;
|
|
506
|
+
}
|
|
507
|
+
resolvedOrganizationId = person.organizationId;
|
|
508
|
+
}
|
|
509
|
+
if (travelers.travelers.length === 0) {
|
|
510
|
+
setError(messages.bookingCreateDialog.validation.travelerRequired);
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
const invalidTraveler = travelers.travelers.find((traveler) => (!traveler.personId && (!traveler.firstName.trim() || !traveler.lastName.trim())) ||
|
|
514
|
+
(traveler.email.trim() ? !isRealBookingEmail(traveler.email) : false));
|
|
515
|
+
if (invalidTraveler) {
|
|
516
|
+
setError(messages.bookingCreateDialog.validation.firstAndLastNameRequired);
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
const overCapacity = getOverCapacityInventoryAssignments(bookingUnits, rooms.quantities, travelers.travelers)[0];
|
|
520
|
+
if (overCapacity) {
|
|
521
|
+
setError(formatMessage(messages.bookingCreateDialog.validation.roomCapacityExceeded, {
|
|
522
|
+
room: overCapacity.unitName,
|
|
523
|
+
assigned: overCapacity.assignedTravelers,
|
|
524
|
+
capacity: overCapacity.capacity,
|
|
525
|
+
}));
|
|
526
|
+
return;
|
|
527
|
+
}
|
|
528
|
+
try {
|
|
529
|
+
if (sharedRoom.enabled && sharedRoom.mode === "join" && !sharedRoom.groupId) {
|
|
530
|
+
setError(messages.bookingCreateDialog.validation.selectSharedRoomGroup);
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
const bookingNumber = generateBookingNumber();
|
|
534
|
+
const confirmedSellAmountCents = pricing?.confirmedAmountCents ?? null;
|
|
535
|
+
const catalogSellAmountCents = pricing?.catalogAmountCents ?? null;
|
|
536
|
+
const priceOverrideReason = pricing?.priceOverrideReason.trim() ?? "";
|
|
537
|
+
if (pricing?.requiresReason) {
|
|
538
|
+
setError(messages.bookingCreateDialog.labels.breakdownOverrideReasonRequired);
|
|
539
|
+
return;
|
|
540
|
+
}
|
|
541
|
+
const paymentSchedules = paymentScheduleToRows(paymentSchedule, pricingCurrency, confirmedSellAmountCents);
|
|
542
|
+
// Resolve the draft once, then derive every shape the wire
|
|
543
|
+
// format needs from the result. Person-priced options get
|
|
544
|
+
// per-band quantities (1 adult + 1 child + 1 infant, not
|
|
545
|
+
// "3 x Adult"); accommodation options keep operator-picked
|
|
546
|
+
// room quantities. Server gets `clientLineKey` + `travelerKeys`
|
|
547
|
+
// on each line so it can write `booking_item_travelers` rows.
|
|
548
|
+
const submitUnits = bookingUnits.length > 0
|
|
549
|
+
? bookingUnits
|
|
550
|
+
: getTravelerAssignableStepperUnits((slotUnitAvailability.data?.data ?? []).map((unit) => ({
|
|
551
|
+
...unit,
|
|
552
|
+
optionId: product.optionId,
|
|
553
|
+
})));
|
|
554
|
+
const redistributed = resolveBookingDraft({
|
|
555
|
+
quantities: rooms.quantities,
|
|
556
|
+
travelers: travelers.travelers,
|
|
557
|
+
units: submitUnits,
|
|
558
|
+
});
|
|
559
|
+
const travelerKeysByUnitId = Object.fromEntries(Object.entries(redistributed.travelerIndexesByUnitId).map(([unitId, indexes]) => [
|
|
560
|
+
unitId,
|
|
561
|
+
indexes.every((index) => Boolean(redistributed.travelers[index]?.clientTravelerKey))
|
|
562
|
+
? indexes
|
|
563
|
+
.map((index) => redistributed.travelers[index]?.clientTravelerKey)
|
|
564
|
+
.filter((key) => Boolean(key))
|
|
565
|
+
: [],
|
|
566
|
+
]));
|
|
567
|
+
const travelerIndexesByUnitAndCategoryId = {};
|
|
568
|
+
const travelerKeysByUnitAndCategoryId = {};
|
|
569
|
+
for (const [unitId, indexes] of Object.entries(redistributed.travelerIndexesByUnitId)) {
|
|
570
|
+
for (const index of indexes) {
|
|
571
|
+
const traveler = redistributed.travelers[index];
|
|
572
|
+
if (!traveler)
|
|
573
|
+
continue;
|
|
574
|
+
const pricingCategoryId = inferTravelerPricingCategoryId(traveler, travelerPricingCategories);
|
|
575
|
+
if (!pricingCategoryId)
|
|
576
|
+
continue;
|
|
577
|
+
travelerIndexesByUnitAndCategoryId[unitId] ??= {};
|
|
578
|
+
travelerIndexesByUnitAndCategoryId[unitId][pricingCategoryId] ??= [];
|
|
579
|
+
travelerIndexesByUnitAndCategoryId[unitId][pricingCategoryId].push(index);
|
|
580
|
+
const key = traveler.clientTravelerKey;
|
|
581
|
+
if (key) {
|
|
582
|
+
travelerKeysByUnitAndCategoryId[unitId] ??= {};
|
|
583
|
+
travelerKeysByUnitAndCategoryId[unitId][pricingCategoryId] ??= [];
|
|
584
|
+
travelerKeysByUnitAndCategoryId[unitId][pricingCategoryId].push(key);
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
const travelerKeys = redistributed.travelers
|
|
589
|
+
.map((traveler) => traveler.clientTravelerKey)
|
|
590
|
+
.filter((key) => Boolean(key));
|
|
591
|
+
const itemLines = itemLinesToRows(redistributed.quantities, submitUnits, pricing, redistributed.travelerIndexesByUnitId, travelerKeysByUnitId, travelerIndexesByUnitAndCategoryId, travelerKeysByUnitAndCategoryId);
|
|
592
|
+
const resolvedExtraLines = resolveBookingExtraLines({
|
|
593
|
+
extraLines,
|
|
594
|
+
travelerCount: travelers.travelers.length,
|
|
595
|
+
travelerKeys: travelerKeys.length === redistributed.travelers.length ? travelerKeys : undefined,
|
|
596
|
+
});
|
|
597
|
+
const travelerRows = travelersToRows({ travelers: redistributed.travelers });
|
|
598
|
+
const voucherRedemption = voucher.picked && voucher.picked.remainingAmountCents != null
|
|
599
|
+
? {
|
|
600
|
+
voucherId: voucher.picked.id,
|
|
601
|
+
amountCents: voucher.picked.remainingAmountCents,
|
|
602
|
+
}
|
|
603
|
+
: undefined;
|
|
604
|
+
const selectedSharedRoomUnitId = getSelectedSharedRoomUnitId(rooms.quantities);
|
|
605
|
+
const groupMembership = sharedRoom.enabled
|
|
606
|
+
? sharedRoom.mode === "create"
|
|
607
|
+
? {
|
|
608
|
+
action: "create",
|
|
609
|
+
kind: "shared_room",
|
|
610
|
+
label: sharedRoom.groupLabel?.trim() ||
|
|
611
|
+
`${messages.bookingCreateDialog.labels.sharedRoomGeneratedLabelPrefix} - ${bookingNumber}`,
|
|
612
|
+
optionUnitId: selectedSharedRoomUnitId,
|
|
613
|
+
makeBookingPrimary: true,
|
|
614
|
+
}
|
|
615
|
+
: sharedRoom.groupId
|
|
616
|
+
? { action: "join", groupId: sharedRoom.groupId, role: "shared" }
|
|
617
|
+
: undefined
|
|
618
|
+
: undefined;
|
|
619
|
+
// Smart-default status from payment state — any payment marked
|
|
620
|
+
// "Already paid" implies the booking is effectively confirmed,
|
|
621
|
+
// otherwise it lands in `awaiting_payment` so the operator can
|
|
622
|
+
// dispatch a payment link. The server commits this status in
|
|
623
|
+
// the create transaction and emits `booking.confirmed`
|
|
624
|
+
// post-commit when applicable — no second roundtrip.
|
|
625
|
+
const initialStatus = hasAnyPaidPayment(paymentSchedule)
|
|
626
|
+
? "confirmed"
|
|
627
|
+
: "awaiting_payment";
|
|
628
|
+
// Build the billing-contact snapshot from whichever CRM record
|
|
629
|
+
// the operator picked, plus the primary identity address when
|
|
630
|
+
// present. Falls back to nulls when a record is missing — the
|
|
631
|
+
// server stores nulls and the detail page hydrates from the live
|
|
632
|
+
// CRM record at read time.
|
|
633
|
+
const addressSnapshot = billingPrimaryAddress
|
|
634
|
+
? {
|
|
635
|
+
contactAddressLine1: billingPrimaryAddress.line1,
|
|
636
|
+
contactAddressLine2: billingPrimaryAddress.line2,
|
|
637
|
+
contactCity: billingPrimaryAddress.city,
|
|
638
|
+
contactRegion: billingPrimaryAddress.region,
|
|
639
|
+
contactPostalCode: billingPrimaryAddress.postalCode,
|
|
640
|
+
contactCountry: billingPrimaryAddress.country,
|
|
641
|
+
}
|
|
642
|
+
: {};
|
|
643
|
+
const contactSnapshot = billingPersonRecord
|
|
644
|
+
? {
|
|
645
|
+
contactPartyType: "individual",
|
|
646
|
+
contactTaxId: null,
|
|
647
|
+
contactFirstName: billingPersonRecord.firstName,
|
|
648
|
+
contactLastName: billingPersonRecord.lastName,
|
|
649
|
+
contactEmail: billingPersonRecord.email,
|
|
650
|
+
contactPhone: billingPersonRecord.phone,
|
|
651
|
+
contactPreferredLanguage: billingPersonRecord.preferredLanguage,
|
|
652
|
+
...addressSnapshot,
|
|
653
|
+
}
|
|
654
|
+
: billingOrganizationRecord
|
|
655
|
+
? {
|
|
656
|
+
contactPartyType: "company",
|
|
657
|
+
contactTaxId: billingOrganizationRecord.taxId,
|
|
658
|
+
contactFirstName: billingOrganizationRecord.name,
|
|
659
|
+
contactLastName: null,
|
|
660
|
+
contactEmail: null,
|
|
661
|
+
contactPhone: null,
|
|
662
|
+
contactPreferredLanguage: billingOrganizationRecord.preferredLanguage,
|
|
663
|
+
...addressSnapshot,
|
|
664
|
+
}
|
|
665
|
+
: {};
|
|
666
|
+
const { booking } = await createBookingMutation.mutateAsync({
|
|
667
|
+
productId: product.productId,
|
|
668
|
+
bookingNumber,
|
|
669
|
+
optionId: product.optionId,
|
|
670
|
+
slotId,
|
|
671
|
+
personId: resolvedPersonId,
|
|
672
|
+
organizationId: resolvedOrganizationId,
|
|
673
|
+
internalNotes: notes.trim() || null,
|
|
674
|
+
catalogSellAmountCents,
|
|
675
|
+
confirmedSellAmountCents,
|
|
676
|
+
priceOverrideReason: priceOverrideReason || null,
|
|
677
|
+
itemLines: itemLines.length > 0 ? itemLines : undefined,
|
|
678
|
+
extraLines: resolvedExtraLines.length > 0 ? resolvedExtraLines : undefined,
|
|
679
|
+
travelers: travelerRows.length > 0 ? travelerRows : undefined,
|
|
680
|
+
paymentSchedules: paymentSchedules.length > 0 ? paymentSchedules : undefined,
|
|
681
|
+
voucherRedemption,
|
|
682
|
+
groupMembership,
|
|
683
|
+
documentGeneration: generateProforma
|
|
684
|
+
? { contractDocument: false, invoiceDocument: true, invoiceType: "proforma" }
|
|
685
|
+
: generateInvoiceAndContract
|
|
686
|
+
? {
|
|
687
|
+
contractDocument: true,
|
|
688
|
+
invoiceDocument: true,
|
|
689
|
+
invoiceType: "invoice",
|
|
690
|
+
}
|
|
691
|
+
: { contractDocument: false, invoiceDocument: false },
|
|
692
|
+
initialStatus,
|
|
693
|
+
// Suppression only matters when transitioning to `confirmed` —
|
|
694
|
+
// `awaiting_payment` doesn't trigger the auto-dispatch
|
|
695
|
+
// subscriber today.
|
|
696
|
+
suppressNotifications: initialStatus === "confirmed" && !notifyTraveler ? true : undefined,
|
|
697
|
+
...contactSnapshot,
|
|
698
|
+
});
|
|
699
|
+
// The booking mutation invalidates booking caches, but the
|
|
700
|
+
// availability surface (slot allocation manifest, slot detail,
|
|
701
|
+
// unit availability, slot list) hosts its own cache under
|
|
702
|
+
// `availabilityQueryKeys.slots()`. Without this, the slot-detail
|
|
703
|
+
// page that opened the sheet keeps showing stale traveler counts /
|
|
704
|
+
// empty rooms / unconsumed capacity until the user refreshes.
|
|
705
|
+
// Nuking the whole slots subtree is cheap and avoids tracking
|
|
706
|
+
// exactly which keys to bust (slotAllocation, slotDetail,
|
|
707
|
+
// slotUnitAvailability, slotsList, …).
|
|
708
|
+
await queryClient.invalidateQueries({ queryKey: availabilityQueryKeys.slots() });
|
|
709
|
+
onCreated?.(booking);
|
|
710
|
+
}
|
|
711
|
+
catch (err) {
|
|
712
|
+
if (err instanceof VoyantApiError && isPayloadResolverMismatchBody(err.body)) {
|
|
713
|
+
setPayloadMismatchUnitIds(Array.from(new Set(err.body.mismatches.map((mismatch) => mismatch.optionUnitId))));
|
|
714
|
+
setError(formatPayloadResolverMismatchError(err.body, roomUnitLabels, messages.bookingCreateDialog.validation));
|
|
715
|
+
return;
|
|
716
|
+
}
|
|
717
|
+
setError(err instanceof Error ? err.message : messages.bookingCreateDialog.validation.createFailed);
|
|
718
|
+
}
|
|
719
|
+
};
|
|
720
|
+
const isSubmitting = createBookingMutation.isPending;
|
|
721
|
+
return (_jsxs("div", { className: "grid min-h-0 flex-1 gap-6 lg:grid-cols-12", children: [_jsxs("div", { className: "flex min-h-0 min-w-0 flex-col lg:col-span-8", children: [_jsxs("div", { className: "flex flex-1 flex-col gap-4 overflow-y-auto px-1 pb-2", children: [_jsx(ProductPickerSection, { value: product, onChange: (next) => {
|
|
722
|
+
setPayloadMismatchUnitIds([]);
|
|
723
|
+
setProduct(next);
|
|
724
|
+
}, enabled: enabled, lockProduct: Boolean(defaultProductId || defaultSlotId), labels: {
|
|
725
|
+
optionNone: messages.bookingCreateDialog.labels.noSpecificOption,
|
|
726
|
+
}, showOptionPicker: false }), product.productId ? (_jsxs("div", { className: "flex flex-col gap-1", children: [_jsx(Label, { children: messages.bookingCreateDialog.fields.departure }), _jsx(AsyncCombobox, { value: slotId, onChange: (v) => setSelectedSlot(v), items: slots, selectedItem: selectedSlot, getKey: (slot) => slot.id, getLabel: (slot) => formatSlotLabel(slot), placeholder: messages.bookingCreateDialog.placeholders.departure, emptyText: messages.bookingCreateDialog.placeholders.departureEmpty, triggerClassName: "w-full", disabled: Boolean(defaultSlotId), clearable: !defaultSlotId })] })) : null, product.productId && slotId ? (_jsx(OptionUnitsStepperSection, { value: rooms, onChange: (next) => {
|
|
727
|
+
setPayloadMismatchUnitIds([]);
|
|
728
|
+
setRooms(next);
|
|
729
|
+
}, productId: product.productId, slotId: slotId, optionId: product.optionId, enabled: enabled, onUnitsChange: handleRoomUnitsChange, slotHasFiniteCapacity: Boolean(selectedSlot) &&
|
|
730
|
+
!selectedSlot?.unlimited &&
|
|
731
|
+
typeof selectedSlot?.remainingPax === "number", invalidOptionUnitIds: payloadMismatchUnitIds, labels: {
|
|
732
|
+
heading: messages.bookingCreateDialog.labels.roomsHeading,
|
|
733
|
+
noOption: messages.bookingCreateDialog.labels.roomsNoOption,
|
|
734
|
+
noSlot: messages.bookingCreateDialog.labels.roomsNoSlot,
|
|
735
|
+
noUnits: messages.bookingCreateDialog.labels.roomsNoUnits,
|
|
736
|
+
remaining: messages.bookingCreateDialog.labels.roomsRemaining,
|
|
737
|
+
unlimited: messages.bookingCreateDialog.labels.roomsUnlimited,
|
|
738
|
+
} })) : null, product.productId && slotId ? (_jsx(ProductExtrasPickerSection, { productId: product.productId, optionId: product.optionId, currency: pricingCurrency, travelerCount: travelers.travelers.length, value: extraLines, onChange: setExtraLines, enabled: enabled, labels: {
|
|
739
|
+
heading: messages.bookingCreateDialog.labels.extrasHeading,
|
|
740
|
+
empty: messages.bookingCreateDialog.labels.extrasEmpty,
|
|
741
|
+
included: messages.bookingCreateDialog.labels.extrasIncluded,
|
|
742
|
+
onRequest: messages.bookingCreateDialog.labels.extrasOnRequest,
|
|
743
|
+
perPerson: messages.bookingCreateDialog.labels.extrasPerPerson,
|
|
744
|
+
} })) : null, product.productId && slotId ? (_jsxs("div", { className: "flex flex-col gap-2 rounded-md border p-3", children: [_jsx(Label, { children: messages.bookingCreateDialog.labels.billingHeading }), _jsx(PersonPickerSection, { value: person, onChange: setPerson, enabled: enabled, labels: {
|
|
745
|
+
createNewPerson: messages.bookingCreateDialog.labels.createNewPerson,
|
|
746
|
+
selectExistingPerson: messages.bookingCreateDialog.labels.selectExistingPerson,
|
|
747
|
+
organizationNone: messages.bookingCreateDialog.labels.organizationNone,
|
|
748
|
+
} })] })) : null, product.productId && slotId ? (_jsx(SharedRoomSection, { value: sharedRoom, onChange: setSharedRoom, productId: product.productId || undefined, enabled: enabled, labels: {
|
|
749
|
+
toggle: messages.bookingCreateDialog.labels.sharedRoomToggle,
|
|
750
|
+
createMode: messages.bookingCreateDialog.labels.sharedRoomCreateMode,
|
|
751
|
+
joinMode: messages.bookingCreateDialog.labels.sharedRoomJoinMode,
|
|
752
|
+
selectPlaceholder: messages.bookingCreateDialog.labels.sharedRoomSelectPlaceholder,
|
|
753
|
+
noGroups: messages.bookingCreateDialog.labels.sharedRoomNoGroups,
|
|
754
|
+
createHint: messages.bookingCreateDialog.labels.sharedRoomCreateHint,
|
|
755
|
+
remove: messages.bookingCreateDialog.labels.sharedRoomRemove,
|
|
756
|
+
} })) : null, product.productId && slotId ? (_jsx(TravelersSection, { value: travelers, onChange: (next) => {
|
|
757
|
+
setPayloadMismatchUnitIds([]);
|
|
758
|
+
setTravelers(next);
|
|
759
|
+
}, roomUnits: roomUnitOptions.length > 0 ? roomUnitOptions : undefined, roomGroups: roomGroups.length > 0 ? roomGroups : undefined, pricingCategories: hasRoomPricingMatrix || roomUnitOptions.length > 0
|
|
760
|
+
? travelerPricingCategories
|
|
761
|
+
: undefined, billingPersonId: (person.billTo ?? "person") === "person" ? person.personId : null, labels: {
|
|
762
|
+
heading: messages.bookingCreateDialog.labels.travelerHeading,
|
|
763
|
+
addTraveler: messages.bookingCreateDialog.labels.addTraveler,
|
|
764
|
+
person: messages.bookingCreateDialog.labels.travelerPerson,
|
|
765
|
+
personSearchPlaceholder: messages.bookingCreateDialog.labels.travelerPersonSearchPlaceholder,
|
|
766
|
+
personEmpty: messages.bookingCreateDialog.labels.travelerPersonEmpty,
|
|
767
|
+
createNewPerson: messages.bookingCreateDialog.labels.createNewPerson,
|
|
768
|
+
createPersonSheetTitle: messages.bookingCreateDialog.labels.createPersonSheetTitle,
|
|
769
|
+
addBillingPerson: messages.bookingCreateDialog.labels.addBillingPersonAsTraveler,
|
|
770
|
+
role: messages.bookingCreateDialog.labels.travelerRole,
|
|
771
|
+
roleLead: messages.bookingCreateDialog.labels.travelerLead,
|
|
772
|
+
roleAdult: messages.bookingCreateDialog.labels.travelerAdult,
|
|
773
|
+
roleChild: messages.bookingCreateDialog.labels.travelerChild,
|
|
774
|
+
roleInfant: messages.bookingCreateDialog.labels.travelerInfant,
|
|
775
|
+
room: messages.bookingCreateDialog.labels.travelerRoom,
|
|
776
|
+
noRoom: messages.bookingCreateDialog.labels.travelerNoRoom,
|
|
777
|
+
remove: messages.bookingCreateDialog.labels.travelerRemove,
|
|
778
|
+
empty: messages.bookingCreateDialog.labels.travelerEmpty,
|
|
779
|
+
} })) : null, product.productId && slotId ? (_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.bookingCreateDialog.fields.internalNotes }), _jsx(Textarea, { value: notes, onChange: (e) => setNotes(e.target.value), placeholder: messages.bookingCreateDialog.placeholders.internalNotes })] })) : null, product.productId && slotId ? (_jsxs("div", { className: "flex flex-col gap-3 rounded-md border p-3", children: [_jsx(Label, { children: messages.bookingCreateDialog.labels.documentGenerationHeading }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsxs("div", { className: "flex items-center gap-2 text-sm", children: [_jsx(Checkbox, { id: "new-booking-generate-proforma", checked: generateProforma, onCheckedChange: (value) => setGenerateProforma(value === true) }), _jsx(Label, { htmlFor: "new-booking-generate-proforma", className: "cursor-pointer", children: messages.bookingCreateDialog.labels.generateProforma })] }), _jsxs("div", { className: "flex items-center gap-2 text-sm", children: [_jsx(Checkbox, { id: "new-booking-generate-invoice-and-contract", checked: generateInvoiceAndContract, onCheckedChange: (value) => setGenerateInvoiceAndContract(value === true) }), _jsx(Label, { htmlFor: "new-booking-generate-invoice-and-contract", className: "cursor-pointer", children: messages.bookingCreateDialog.labels.generateInvoiceAndContract })] }), hasAnyPaidPayment(paymentSchedule) ? (_jsxs("div", { className: "flex items-start gap-2 border-t pt-2 text-sm", children: [_jsx(Checkbox, { id: "new-booking-notify-traveler", checked: notifyTraveler, onCheckedChange: (v) => setNotifyTraveler(v === true), className: "mt-0.5" }), _jsxs("div", { className: "flex flex-col gap-1", children: [_jsx(Label, { htmlFor: "new-booking-notify-traveler", className: "cursor-pointer text-sm", children: messages.bookingCreateDialog.fields.notifyTraveler }), _jsx("p", { className: "text-xs text-muted-foreground", children: messages.bookingCreateDialog.fields.notifyTravelerHint })] })] })) : null] })] })) : null] }), error ? (_jsx("div", { role: "alert", className: "mt-3 rounded-md border border-destructive/50 bg-destructive/10 px-3 py-2 text-xs text-destructive", children: error })) : null, _jsxs("div", { className: "mt-4 flex items-center justify-end gap-2 border-t px-1 pt-3", children: [_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: onCancel, disabled: isSubmitting, children: messages.common.cancel }), _jsxs(Button, { type: "button", size: "sm", onClick: handleSubmit, disabled: isSubmitting || !product.productId || !slotId || !hasSelectedUnits, children: [isSubmitting && _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), hasAnyPaidPayment(paymentSchedule)
|
|
780
|
+
? messages.bookingCreateDialog.actions.createConfirmedBooking
|
|
781
|
+
: messages.bookingCreateDialog.actions.createAwaitingPaymentBooking] })] })] }), _jsxs("div", { className: "flex flex-col gap-4 lg:col-span-4", children: [_jsx(BookingPreviewCard, { productId: product.productId, optionId: product.optionId, slotId: slotId, slotLabel: (() => {
|
|
782
|
+
const slot = slots.find((s) => s.id === slotId);
|
|
783
|
+
return slot ? formatSlotLabel(slot) : null;
|
|
784
|
+
})(), unitQuantities: displayQuantities, unitLabels: roomUnitLabels, pricingCategoryQuantities: travelerPricingCategoryQuantities, pricingCategoryLabels: travelerPricingCategoryLabels, extraLines: displayExtraLines, travelers: travelers.travelers, messages: messages, onPricingChange: setPricing }), product.productId && slotId ? (_jsx(VoucherPickerSection, { value: voucher, onChange: setVoucher, currency: currency, labels: {
|
|
785
|
+
heading: messages.bookingCreateDialog.labels.voucherHeading,
|
|
786
|
+
codePlaceholder: messages.bookingCreateDialog.labels.voucherCodePlaceholder,
|
|
787
|
+
apply: messages.bookingCreateDialog.labels.voucherApply,
|
|
788
|
+
clear: messages.bookingCreateDialog.labels.voucherClear,
|
|
789
|
+
remainingLabel: messages.bookingCreateDialog.labels.voucherRemainingLabel,
|
|
790
|
+
invalidLabel: messages.bookingCreateDialog.labels.voucherInvalidLabel,
|
|
791
|
+
} })) : null, product.productId && slotId ? (_jsx(PaymentScheduleSection, { value: paymentSchedule, onChange: setPaymentSchedule, currency: pricingCurrency, totalAmountCents: pricingTotalAmountCents, departureDate: selectedSlot?.startsAt?.slice(0, 10) ?? null, labels: {
|
|
792
|
+
heading: messages.bookingCreateDialog.labels.paymentHeading,
|
|
793
|
+
modeUnpaid: messages.bookingCreateDialog.labels.paymentModeUnpaid,
|
|
794
|
+
modeFull: messages.bookingCreateDialog.labels.paymentModeFull,
|
|
795
|
+
modeAdvance: messages.bookingCreateDialog.labels.paymentModeAdvance,
|
|
796
|
+
modeSplit: messages.bookingCreateDialog.labels.paymentModeSplit,
|
|
797
|
+
dueDate: messages.bookingCreateDialog.labels.paymentDueDate,
|
|
798
|
+
amount: messages.bookingCreateDialog.labels.paymentAmount,
|
|
799
|
+
firstInstallment: messages.bookingCreateDialog.labels.paymentFirstInstallment,
|
|
800
|
+
secondInstallment: messages.bookingCreateDialog.labels.paymentSecondInstallment,
|
|
801
|
+
preset5050: messages.bookingCreateDialog.labels.paymentPreset5050,
|
|
802
|
+
unpaidHint: messages.bookingCreateDialog.labels.paymentUnpaidHint,
|
|
803
|
+
totalDue: messages.bookingCreateDialog.labels.paymentTotalDue,
|
|
804
|
+
scheduledTotal: messages.bookingCreateDialog.labels.paymentScheduledTotal,
|
|
805
|
+
remaining: messages.bookingCreateDialog.labels.paymentRemaining,
|
|
806
|
+
alreadyPaid: messages.bookingCreateDialog.labels.paymentAlreadyPaid,
|
|
807
|
+
paymentDate: messages.bookingCreateDialog.labels.paymentDate,
|
|
808
|
+
paymentMethod: messages.bookingCreateDialog.labels.paymentMethod,
|
|
809
|
+
paymentReference: messages.bookingCreateDialog.labels.paymentReference,
|
|
810
|
+
} })) : null] })] }));
|
|
811
|
+
}
|