@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.
Files changed (602) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +87 -0
  3. package/dist/admin/booking-contract-dialog.d.ts +22 -0
  4. package/dist/admin/booking-contract-dialog.d.ts.map +1 -0
  5. package/dist/admin/booking-contract-dialog.js +161 -0
  6. package/dist/admin/booking-detail-host.d.ts +103 -0
  7. package/dist/admin/booking-detail-host.d.ts.map +1 -0
  8. package/dist/admin/booking-detail-host.js +127 -0
  9. package/dist/admin/booking-detail-skeleton.d.ts +7 -0
  10. package/dist/admin/booking-detail-skeleton.d.ts.map +1 -0
  11. package/dist/admin/booking-detail-skeleton.js +24 -0
  12. package/dist/admin/booking-documents-table.d.ts +13 -0
  13. package/dist/admin/booking-documents-table.d.ts.map +1 -0
  14. package/dist/admin/booking-documents-table.js +259 -0
  15. package/dist/admin/booking-invoice-sheet.d.ts +18 -0
  16. package/dist/admin/booking-invoice-sheet.d.ts.map +1 -0
  17. package/dist/admin/booking-invoice-sheet.js +101 -0
  18. package/dist/admin/booking-journey-host.d.ts +24 -0
  19. package/dist/admin/booking-journey-host.d.ts.map +1 -0
  20. package/dist/admin/booking-journey-host.js +278 -0
  21. package/dist/admin/bookings-host.d.ts +26 -0
  22. package/dist/admin/bookings-host.d.ts.map +1 -0
  23. package/dist/admin/bookings-host.js +18 -0
  24. package/dist/admin/bookings-list-skeleton.d.ts +10 -0
  25. package/dist/admin/bookings-list-skeleton.d.ts.map +1 -0
  26. package/dist/admin/bookings-list-skeleton.js +25 -0
  27. package/dist/admin/index.d.ts +273 -0
  28. package/dist/admin/index.d.ts.map +1 -0
  29. package/dist/admin/index.js +331 -0
  30. package/dist/admin/journey-billing-duplicate-warning.d.ts +3 -0
  31. package/dist/admin/journey-billing-duplicate-warning.d.ts.map +1 -0
  32. package/dist/admin/journey-billing-duplicate-warning.js +26 -0
  33. package/dist/admin/journey-departure-picker.d.ts +7 -0
  34. package/dist/admin/journey-departure-picker.d.ts.map +1 -0
  35. package/dist/admin/journey-departure-picker.js +100 -0
  36. package/dist/admin/journey-units-picker.d.ts +11 -0
  37. package/dist/admin/journey-units-picker.d.ts.map +1 -0
  38. package/dist/admin/journey-units-picker.js +60 -0
  39. package/dist/admin/journey-voucher-picker.d.ts +3 -0
  40. package/dist/admin/journey-voucher-picker.d.ts.map +1 -0
  41. package/dist/admin/journey-voucher-picker.js +71 -0
  42. package/dist/admin/pages/booking-compose-page.d.ts +9 -0
  43. package/dist/admin/pages/booking-compose-page.d.ts.map +1 -0
  44. package/dist/admin/pages/booking-compose-page.js +17 -0
  45. package/dist/admin/pages/booking-detail-page.d.ts +11 -0
  46. package/dist/admin/pages/booking-detail-page.d.ts.map +1 -0
  47. package/dist/admin/pages/booking-detail-page.js +14 -0
  48. package/dist/admin/pages/booking-journey-page.d.ts +12 -0
  49. package/dist/admin/pages/booking-journey-page.d.ts.map +1 -0
  50. package/dist/admin/pages/booking-journey-page.js +26 -0
  51. package/dist/admin/pages/booking-new-page.d.ts +15 -0
  52. package/dist/admin/pages/booking-new-page.d.ts.map +1 -0
  53. package/dist/admin/pages/booking-new-page.js +50 -0
  54. package/dist/admin/pages/bookings-index-page.d.ts +20 -0
  55. package/dist/admin/pages/bookings-index-page.d.ts.map +1 -0
  56. package/dist/admin/pages/bookings-index-page.js +18 -0
  57. package/dist/admin/person-bookings-widget.d.ts +13 -0
  58. package/dist/admin/person-bookings-widget.d.ts.map +1 -0
  59. package/dist/admin/person-bookings-widget.js +48 -0
  60. package/dist/admin/slots.d.ts +31 -0
  61. package/dist/admin/slots.d.ts.map +1 -0
  62. package/dist/admin/slots.js +30 -0
  63. package/dist/admin/use-booking-action-ledger-events.d.ts +15 -0
  64. package/dist/admin/use-booking-action-ledger-events.d.ts.map +1 -0
  65. package/dist/admin/use-booking-action-ledger-events.js +66 -0
  66. package/dist/client.d.ts +14 -0
  67. package/dist/client.d.ts.map +1 -0
  68. package/dist/client.js +59 -0
  69. package/dist/components/booking-activity-timeline.d.ts +32 -0
  70. package/dist/components/booking-activity-timeline.d.ts.map +1 -0
  71. package/dist/components/booking-activity-timeline.js +147 -0
  72. package/dist/components/booking-billing-dialog.d.ts +16 -0
  73. package/dist/components/booking-billing-dialog.d.ts.map +1 -0
  74. package/dist/components/booking-billing-dialog.js +315 -0
  75. package/dist/components/booking-cancellation-dialog.d.ts +18 -0
  76. package/dist/components/booking-cancellation-dialog.d.ts.map +1 -0
  77. package/dist/components/booking-cancellation-dialog.js +79 -0
  78. package/dist/components/booking-combobox.d.ts +13 -0
  79. package/dist/components/booking-combobox.d.ts.map +1 -0
  80. package/dist/components/booking-combobox.js +44 -0
  81. package/dist/components/booking-create-form-utils.d.ts +56 -0
  82. package/dist/components/booking-create-form-utils.d.ts.map +1 -0
  83. package/dist/components/booking-create-form-utils.js +216 -0
  84. package/dist/components/booking-create-page.d.ts +14 -0
  85. package/dist/components/booking-create-page.d.ts.map +1 -0
  86. package/dist/components/booking-create-page.js +11 -0
  87. package/dist/components/booking-create-preview-card.d.ts +26 -0
  88. package/dist/components/booking-create-preview-card.d.ts.map +1 -0
  89. package/dist/components/booking-create-preview-card.js +107 -0
  90. package/dist/components/booking-create-product-extras-picker.d.ts +18 -0
  91. package/dist/components/booking-create-product-extras-picker.d.ts.map +1 -0
  92. package/dist/components/booking-create-product-extras-picker.js +82 -0
  93. package/dist/components/booking-create-sheet.d.ts +34 -0
  94. package/dist/components/booking-create-sheet.d.ts.map +1 -0
  95. package/dist/components/booking-create-sheet.js +811 -0
  96. package/dist/components/booking-create-utils.d.ts +66 -0
  97. package/dist/components/booking-create-utils.d.ts.map +1 -0
  98. package/dist/components/booking-create-utils.js +185 -0
  99. package/dist/components/booking-detail-page.d.ts +126 -0
  100. package/dist/components/booking-detail-page.d.ts.map +1 -0
  101. package/dist/components/booking-detail-page.js +264 -0
  102. package/dist/components/booking-dialog.d.ts +28 -0
  103. package/dist/components/booking-dialog.d.ts.map +1 -0
  104. package/dist/components/booking-dialog.js +130 -0
  105. package/dist/components/booking-document-dialog.d.ts +8 -0
  106. package/dist/components/booking-document-dialog.d.ts.map +1 -0
  107. package/dist/components/booking-document-dialog.js +83 -0
  108. package/dist/components/booking-document-list.d.ts +5 -0
  109. package/dist/components/booking-document-list.d.ts.map +1 -0
  110. package/dist/components/booking-document-list.js +43 -0
  111. package/dist/components/booking-group-link-dialog.d.ts +10 -0
  112. package/dist/components/booking-group-link-dialog.d.ts.map +1 -0
  113. package/dist/components/booking-group-link-dialog.js +79 -0
  114. package/dist/components/booking-group-section.d.ts +27 -0
  115. package/dist/components/booking-group-section.d.ts.map +1 -0
  116. package/dist/components/booking-group-section.js +51 -0
  117. package/dist/components/booking-guarantee-dialog.d.ts +10 -0
  118. package/dist/components/booking-guarantee-dialog.d.ts.map +1 -0
  119. package/dist/components/booking-guarantee-dialog.js +123 -0
  120. package/dist/components/booking-guarantee-list.d.ts +5 -0
  121. package/dist/components/booking-guarantee-list.d.ts.map +1 -0
  122. package/dist/components/booking-guarantee-list.js +86 -0
  123. package/dist/components/booking-item-dialog.d.ts +10 -0
  124. package/dist/components/booking-item-dialog.d.ts.map +1 -0
  125. package/dist/components/booking-item-dialog.js +155 -0
  126. package/dist/components/booking-item-list.d.ts +12 -0
  127. package/dist/components/booking-item-list.d.ts.map +1 -0
  128. package/dist/components/booking-item-list.js +191 -0
  129. package/dist/components/booking-item-travelers.d.ts +6 -0
  130. package/dist/components/booking-item-travelers.d.ts.map +1 -0
  131. package/dist/components/booking-item-travelers.js +57 -0
  132. package/dist/components/booking-list-filters.d.ts +43 -0
  133. package/dist/components/booking-list-filters.d.ts.map +1 -0
  134. package/dist/components/booking-list-filters.js +192 -0
  135. package/dist/components/booking-list.d.ts +50 -0
  136. package/dist/components/booking-list.d.ts.map +1 -0
  137. package/dist/components/booking-list.js +352 -0
  138. package/dist/components/booking-note-dialog.d.ts +16 -0
  139. package/dist/components/booking-note-dialog.d.ts.map +1 -0
  140. package/dist/components/booking-note-dialog.js +41 -0
  141. package/dist/components/booking-notes.d.ts +5 -0
  142. package/dist/components/booking-notes.d.ts.map +1 -0
  143. package/dist/components/booking-notes.js +45 -0
  144. package/dist/components/booking-payment-reconciliation-banner.d.ts +5 -0
  145. package/dist/components/booking-payment-reconciliation-banner.d.ts.map +1 -0
  146. package/dist/components/booking-payment-reconciliation-banner.js +91 -0
  147. package/dist/components/booking-payment-schedule-dialog.d.ts +10 -0
  148. package/dist/components/booking-payment-schedule-dialog.d.ts.map +1 -0
  149. package/dist/components/booking-payment-schedule-dialog.js +117 -0
  150. package/dist/components/booking-payment-schedule-list.d.ts +10 -0
  151. package/dist/components/booking-payment-schedule-list.d.ts.map +1 -0
  152. package/dist/components/booking-payment-schedule-list.js +217 -0
  153. package/dist/components/booking-payments-summary.d.ts +83 -0
  154. package/dist/components/booking-payments-summary.d.ts.map +1 -0
  155. package/dist/components/booking-payments-summary.js +176 -0
  156. package/dist/components/booking-quick-view-sheet.d.ts +14 -0
  157. package/dist/components/booking-quick-view-sheet.d.ts.map +1 -0
  158. package/dist/components/booking-quick-view-sheet.js +283 -0
  159. package/dist/components/bookings-page.d.ts +19 -0
  160. package/dist/components/bookings-page.d.ts.map +1 -0
  161. package/dist/components/bookings-page.js +9 -0
  162. package/dist/components/file-dropzone.d.ts +25 -0
  163. package/dist/components/file-dropzone.d.ts.map +1 -0
  164. package/dist/components/file-dropzone.js +102 -0
  165. package/dist/components/icon-action-button.d.ts +18 -0
  166. package/dist/components/icon-action-button.d.ts.map +1 -0
  167. package/dist/components/icon-action-button.js +13 -0
  168. package/dist/components/option-units-stepper-section.d.ts +111 -0
  169. package/dist/components/option-units-stepper-section.d.ts.map +1 -0
  170. package/dist/components/option-units-stepper-section.js +276 -0
  171. package/dist/components/payment-schedule-section.d.ts +91 -0
  172. package/dist/components/payment-schedule-section.d.ts.map +1 -0
  173. package/dist/components/payment-schedule-section.js +206 -0
  174. package/dist/components/person-picker-section.d.ts +71 -0
  175. package/dist/components/person-picker-section.d.ts.map +1 -0
  176. package/dist/components/person-picker-section.js +160 -0
  177. package/dist/components/price-breakdown-section.d.ts +83 -0
  178. package/dist/components/price-breakdown-section.d.ts.map +1 -0
  179. package/dist/components/price-breakdown-section.js +278 -0
  180. package/dist/components/product-picker-section.d.ts +29 -0
  181. package/dist/components/product-picker-section.d.ts.map +1 -0
  182. package/dist/components/product-picker-section.js +74 -0
  183. package/dist/components/shared-room-section.d.ts +40 -0
  184. package/dist/components/shared-room-section.d.ts.map +1 -0
  185. package/dist/components/shared-room-section.js +99 -0
  186. package/dist/components/status-badge.d.ts +24 -0
  187. package/dist/components/status-badge.d.ts.map +1 -0
  188. package/dist/components/status-badge.js +65 -0
  189. package/dist/components/status-change-dialog.d.ts +10 -0
  190. package/dist/components/status-change-dialog.d.ts.map +1 -0
  191. package/dist/components/status-change-dialog.js +57 -0
  192. package/dist/components/supplier-status-dialog.d.ts +10 -0
  193. package/dist/components/supplier-status-dialog.d.ts.map +1 -0
  194. package/dist/components/supplier-status-dialog.js +98 -0
  195. package/dist/components/supplier-status-list.d.ts +5 -0
  196. package/dist/components/supplier-status-list.d.ts.map +1 -0
  197. package/dist/components/supplier-status-list.js +115 -0
  198. package/dist/components/traveler-category-buttons.d.ts +26 -0
  199. package/dist/components/traveler-category-buttons.d.ts.map +1 -0
  200. package/dist/components/traveler-category-buttons.js +35 -0
  201. package/dist/components/traveler-dialog.d.ts +10 -0
  202. package/dist/components/traveler-dialog.d.ts.map +1 -0
  203. package/dist/components/traveler-dialog.js +256 -0
  204. package/dist/components/traveler-list.d.ts +6 -0
  205. package/dist/components/traveler-list.d.ts.map +1 -0
  206. package/dist/components/traveler-list.js +295 -0
  207. package/dist/components/travelers-section-controls.d.ts +52 -0
  208. package/dist/components/travelers-section-controls.d.ts.map +1 -0
  209. package/dist/components/travelers-section-controls.js +206 -0
  210. package/dist/components/travelers-section.d.ts +159 -0
  211. package/dist/components/travelers-section.d.ts.map +1 -0
  212. package/dist/components/travelers-section.js +355 -0
  213. package/dist/components/voucher-picker-section.d.ts +50 -0
  214. package/dist/components/voucher-picker-section.d.ts.map +1 -0
  215. package/dist/components/voucher-picker-section.js +79 -0
  216. package/dist/extras/client.d.ts +14 -0
  217. package/dist/extras/client.d.ts.map +1 -0
  218. package/dist/extras/client.js +58 -0
  219. package/dist/extras/components/extra-catalog-card.d.ts +13 -0
  220. package/dist/extras/components/extra-catalog-card.d.ts.map +1 -0
  221. package/dist/extras/components/extra-catalog-card.js +52 -0
  222. package/dist/extras/components/product-combobox.d.ts +9 -0
  223. package/dist/extras/components/product-combobox.d.ts.map +1 -0
  224. package/dist/extras/components/product-combobox.js +46 -0
  225. package/dist/extras/components/slot-extras-manifest-panel.d.ts +6 -0
  226. package/dist/extras/components/slot-extras-manifest-panel.d.ts.map +1 -0
  227. package/dist/extras/components/slot-extras-manifest-panel.js +108 -0
  228. package/dist/extras/hooks/index.d.ts +5 -0
  229. package/dist/extras/hooks/index.d.ts.map +1 -0
  230. package/dist/extras/hooks/index.js +4 -0
  231. package/dist/extras/hooks/use-product-extra.d.ts +24 -0
  232. package/dist/extras/hooks/use-product-extra.d.ts.map +1 -0
  233. package/dist/extras/hooks/use-product-extra.js +12 -0
  234. package/dist/extras/hooks/use-product-extras.d.ts +30 -0
  235. package/dist/extras/hooks/use-product-extras.d.ts.map +1 -0
  236. package/dist/extras/hooks/use-product-extras.js +12 -0
  237. package/dist/extras/hooks/use-slot-extra-manifest-mutation.d.ts +48 -0
  238. package/dist/extras/hooks/use-slot-extra-manifest-mutation.d.ts.map +1 -0
  239. package/dist/extras/hooks/use-slot-extra-manifest-mutation.js +26 -0
  240. package/dist/extras/hooks/use-slot-extra-manifest.d.ts +68 -0
  241. package/dist/extras/hooks/use-slot-extra-manifest.d.ts.map +1 -0
  242. package/dist/extras/hooks/use-slot-extra-manifest.js +11 -0
  243. package/dist/extras/i18n/en.d.ts +52 -0
  244. package/dist/extras/i18n/en.d.ts.map +1 -0
  245. package/dist/extras/i18n/en.js +51 -0
  246. package/dist/extras/i18n/index.d.ts +5 -0
  247. package/dist/extras/i18n/index.d.ts.map +1 -0
  248. package/dist/extras/i18n/index.js +3 -0
  249. package/dist/extras/i18n/messages.d.ts +37 -0
  250. package/dist/extras/i18n/messages.d.ts.map +1 -0
  251. package/dist/extras/i18n/messages.js +1 -0
  252. package/dist/extras/i18n/provider.d.ts +126 -0
  253. package/dist/extras/i18n/provider.d.ts.map +1 -0
  254. package/dist/extras/i18n/provider.js +44 -0
  255. package/dist/extras/i18n/ro.d.ts +52 -0
  256. package/dist/extras/i18n/ro.d.ts.map +1 -0
  257. package/dist/extras/i18n/ro.js +51 -0
  258. package/dist/extras/index.d.ts +7 -0
  259. package/dist/extras/index.d.ts.map +1 -0
  260. package/dist/extras/index.js +6 -0
  261. package/dist/extras/provider.d.ts +2 -0
  262. package/dist/extras/provider.d.ts.map +1 -0
  263. package/dist/extras/provider.js +1 -0
  264. package/dist/extras/query-keys.d.ts +16 -0
  265. package/dist/extras/query-keys.d.ts.map +1 -0
  266. package/dist/extras/query-keys.js +8 -0
  267. package/dist/extras/query-options.d.ts +455 -0
  268. package/dist/extras/query-options.d.ts.map +1 -0
  269. package/dist/extras/query-options.js +44 -0
  270. package/dist/extras/schemas.d.ts +416 -0
  271. package/dist/extras/schemas.d.ts.map +1 -0
  272. package/dist/extras/schemas.js +89 -0
  273. package/dist/extras/ui.d.ts +4 -0
  274. package/dist/extras/ui.d.ts.map +1 -0
  275. package/dist/extras/ui.js +3 -0
  276. package/dist/extras.d.ts +10 -0
  277. package/dist/extras.d.ts.map +1 -0
  278. package/dist/extras.js +9 -0
  279. package/dist/hooks/index.d.ts +36 -0
  280. package/dist/hooks/index.d.ts.map +1 -0
  281. package/dist/hooks/index.js +35 -0
  282. package/dist/hooks/use-booking-action-ledger.d.ts +63 -0
  283. package/dist/hooks/use-booking-action-ledger.d.ts.map +1 -0
  284. package/dist/hooks/use-booking-action-ledger.js +34 -0
  285. package/dist/hooks/use-booking-activity.d.ts +17 -0
  286. package/dist/hooks/use-booking-activity.d.ts.map +1 -0
  287. package/dist/hooks/use-booking-activity.js +12 -0
  288. package/dist/hooks/use-booking-cancel-mutation.d.ts +69 -0
  289. package/dist/hooks/use-booking-cancel-mutation.d.ts.map +1 -0
  290. package/dist/hooks/use-booking-cancel-mutation.js +24 -0
  291. package/dist/hooks/use-booking-contract-generation.d.ts +31 -0
  292. package/dist/hooks/use-booking-contract-generation.d.ts.map +1 -0
  293. package/dist/hooks/use-booking-contract-generation.js +36 -0
  294. package/dist/hooks/use-booking-convert-mutation.d.ts +81 -0
  295. package/dist/hooks/use-booking-convert-mutation.d.ts.map +1 -0
  296. package/dist/hooks/use-booking-convert-mutation.js +24 -0
  297. package/dist/hooks/use-booking-create-mutation.d.ts +337 -0
  298. package/dist/hooks/use-booking-create-mutation.d.ts.map +1 -0
  299. package/dist/hooks/use-booking-create-mutation.js +43 -0
  300. package/dist/hooks/use-booking-documents.d.ts +41 -0
  301. package/dist/hooks/use-booking-documents.d.ts.map +1 -0
  302. package/dist/hooks/use-booking-documents.js +46 -0
  303. package/dist/hooks/use-booking-dual-create-mutation.d.ts +338 -0
  304. package/dist/hooks/use-booking-dual-create-mutation.d.ts.map +1 -0
  305. package/dist/hooks/use-booking-dual-create-mutation.js +45 -0
  306. package/dist/hooks/use-booking-group-for-booking.d.ts +24 -0
  307. package/dist/hooks/use-booking-group-for-booking.d.ts.map +1 -0
  308. package/dist/hooks/use-booking-group-for-booking.js +12 -0
  309. package/dist/hooks/use-booking-group-member-mutation.d.ts +27 -0
  310. package/dist/hooks/use-booking-group-member-mutation.d.ts.map +1 -0
  311. package/dist/hooks/use-booking-group-member-mutation.js +38 -0
  312. package/dist/hooks/use-booking-group-mutation.d.ts +40 -0
  313. package/dist/hooks/use-booking-group-mutation.d.ts.map +1 -0
  314. package/dist/hooks/use-booking-group-mutation.js +32 -0
  315. package/dist/hooks/use-booking-group.d.ts +85 -0
  316. package/dist/hooks/use-booking-group.d.ts.map +1 -0
  317. package/dist/hooks/use-booking-group.js +12 -0
  318. package/dist/hooks/use-booking-groups.d.ts +21 -0
  319. package/dist/hooks/use-booking-groups.d.ts.map +1 -0
  320. package/dist/hooks/use-booking-groups.js +12 -0
  321. package/dist/hooks/use-booking-item-mutation.d.ts +101 -0
  322. package/dist/hooks/use-booking-item-mutation.d.ts.map +1 -0
  323. package/dist/hooks/use-booking-item-mutation.js +42 -0
  324. package/dist/hooks/use-booking-item-travelers.d.ts +32 -0
  325. package/dist/hooks/use-booking-item-travelers.d.ts.map +1 -0
  326. package/dist/hooks/use-booking-item-travelers.js +48 -0
  327. package/dist/hooks/use-booking-items.d.ts +36 -0
  328. package/dist/hooks/use-booking-items.d.ts.map +1 -0
  329. package/dist/hooks/use-booking-items.js +12 -0
  330. package/dist/hooks/use-booking-mutation.d.ts +158 -0
  331. package/dist/hooks/use-booking-mutation.d.ts.map +1 -0
  332. package/dist/hooks/use-booking-mutation.js +39 -0
  333. package/dist/hooks/use-booking-note-mutation.d.ts +39 -0
  334. package/dist/hooks/use-booking-note-mutation.d.ts.map +1 -0
  335. package/dist/hooks/use-booking-note-mutation.js +44 -0
  336. package/dist/hooks/use-booking-notes.d.ts +15 -0
  337. package/dist/hooks/use-booking-notes.d.ts.map +1 -0
  338. package/dist/hooks/use-booking-notes.js +12 -0
  339. package/dist/hooks/use-booking-primary-product.d.ts +28 -0
  340. package/dist/hooks/use-booking-primary-product.d.ts.map +1 -0
  341. package/dist/hooks/use-booking-primary-product.js +20 -0
  342. package/dist/hooks/use-booking-status-mutation.d.ts +156 -0
  343. package/dist/hooks/use-booking-status-mutation.d.ts.map +1 -0
  344. package/dist/hooks/use-booking-status-mutation.js +54 -0
  345. package/dist/hooks/use-booking-tax-preview.d.ts +29 -0
  346. package/dist/hooks/use-booking-tax-preview.d.ts.map +1 -0
  347. package/dist/hooks/use-booking-tax-preview.js +21 -0
  348. package/dist/hooks/use-booking.d.ts +67 -0
  349. package/dist/hooks/use-booking.d.ts.map +1 -0
  350. package/dist/hooks/use-booking.js +12 -0
  351. package/dist/hooks/use-bookings.d.ts +71 -0
  352. package/dist/hooks/use-bookings.d.ts.map +1 -0
  353. package/dist/hooks/use-bookings.js +12 -0
  354. package/dist/hooks/use-pricing-preview.d.ts +61 -0
  355. package/dist/hooks/use-pricing-preview.d.ts.map +1 -0
  356. package/dist/hooks/use-pricing-preview.js +18 -0
  357. package/dist/hooks/use-public-booking-session-flow-mutation.d.ts +148 -0
  358. package/dist/hooks/use-public-booking-session-flow-mutation.d.ts.map +1 -0
  359. package/dist/hooks/use-public-booking-session-flow-mutation.js +35 -0
  360. package/dist/hooks/use-public-booking-session-state.d.ts +16 -0
  361. package/dist/hooks/use-public-booking-session-state.d.ts.map +1 -0
  362. package/dist/hooks/use-public-booking-session-state.js +12 -0
  363. package/dist/hooks/use-public-booking-session.d.ts +101 -0
  364. package/dist/hooks/use-public-booking-session.d.ts.map +1 -0
  365. package/dist/hooks/use-public-booking-session.js +12 -0
  366. package/dist/hooks/use-reveal-traveler.d.ts +54 -0
  367. package/dist/hooks/use-reveal-traveler.d.ts.map +1 -0
  368. package/dist/hooks/use-reveal-traveler.js +18 -0
  369. package/dist/hooks/use-sharing-groups.d.ts +41 -0
  370. package/dist/hooks/use-sharing-groups.d.ts.map +1 -0
  371. package/dist/hooks/use-sharing-groups.js +20 -0
  372. package/dist/hooks/use-supplier-status-mutation.d.ts +46 -0
  373. package/dist/hooks/use-supplier-status-mutation.d.ts.map +1 -0
  374. package/dist/hooks/use-supplier-status-mutation.js +39 -0
  375. package/dist/hooks/use-supplier-statuses.d.ts +20 -0
  376. package/dist/hooks/use-supplier-statuses.d.ts.map +1 -0
  377. package/dist/hooks/use-supplier-statuses.js +12 -0
  378. package/dist/hooks/use-traveler-mutation.d.ts +55 -0
  379. package/dist/hooks/use-traveler-mutation.d.ts.map +1 -0
  380. package/dist/hooks/use-traveler-mutation.js +42 -0
  381. package/dist/hooks/use-traveler-with-travel-details-mutation.d.ts +120 -0
  382. package/dist/hooks/use-traveler-with-travel-details-mutation.d.ts.map +1 -0
  383. package/dist/hooks/use-traveler-with-travel-details-mutation.js +43 -0
  384. package/dist/hooks/use-travelers.d.ts +23 -0
  385. package/dist/hooks/use-travelers.d.ts.map +1 -0
  386. package/dist/hooks/use-travelers.js +12 -0
  387. package/dist/i18n/en-base.d.ts +295 -0
  388. package/dist/i18n/en-base.d.ts.map +1 -0
  389. package/dist/i18n/en-base.js +294 -0
  390. package/dist/i18n/en-create-list.d.ts +327 -0
  391. package/dist/i18n/en-create-list.d.ts.map +1 -0
  392. package/dist/i18n/en-create-list.js +326 -0
  393. package/dist/i18n/en-journey.d.ts +229 -0
  394. package/dist/i18n/en-journey.d.ts.map +1 -0
  395. package/dist/i18n/en-journey.js +228 -0
  396. package/dist/i18n/en-operations.d.ts +382 -0
  397. package/dist/i18n/en-operations.d.ts.map +1 -0
  398. package/dist/i18n/en-operations.js +381 -0
  399. package/dist/i18n/en-sections.d.ts +360 -0
  400. package/dist/i18n/en-sections.d.ts.map +1 -0
  401. package/dist/i18n/en-sections.js +359 -0
  402. package/dist/i18n/en.d.ts +1581 -0
  403. package/dist/i18n/en.d.ts.map +1 -0
  404. package/dist/i18n/en.js +12 -0
  405. package/dist/i18n/index.d.ts +5 -0
  406. package/dist/i18n/index.d.ts.map +1 -0
  407. package/dist/i18n/index.js +3 -0
  408. package/dist/i18n/messages-base.d.ts +251 -0
  409. package/dist/i18n/messages-base.d.ts.map +1 -0
  410. package/dist/i18n/messages-base.js +1 -0
  411. package/dist/i18n/messages-create-list.d.ts +310 -0
  412. package/dist/i18n/messages-create-list.d.ts.map +1 -0
  413. package/dist/i18n/messages-create-list.js +1 -0
  414. package/dist/i18n/messages-journey.d.ts +198 -0
  415. package/dist/i18n/messages-journey.d.ts.map +1 -0
  416. package/dist/i18n/messages-journey.js +1 -0
  417. package/dist/i18n/messages-operations.d.ts +362 -0
  418. package/dist/i18n/messages-operations.d.ts.map +1 -0
  419. package/dist/i18n/messages-operations.js +1 -0
  420. package/dist/i18n/messages-sections.d.ts +312 -0
  421. package/dist/i18n/messages-sections.d.ts.map +1 -0
  422. package/dist/i18n/messages-sections.js +1 -0
  423. package/dist/i18n/messages.d.ts +7 -0
  424. package/dist/i18n/messages.d.ts.map +1 -0
  425. package/dist/i18n/messages.js +1 -0
  426. package/dist/i18n/provider.d.ts +3185 -0
  427. package/dist/i18n/provider.d.ts.map +1 -0
  428. package/dist/i18n/provider.js +45 -0
  429. package/dist/i18n/ro-base.d.ts +295 -0
  430. package/dist/i18n/ro-base.d.ts.map +1 -0
  431. package/dist/i18n/ro-base.js +294 -0
  432. package/dist/i18n/ro-create-list.d.ts +327 -0
  433. package/dist/i18n/ro-create-list.d.ts.map +1 -0
  434. package/dist/i18n/ro-create-list.js +326 -0
  435. package/dist/i18n/ro-journey.d.ts +229 -0
  436. package/dist/i18n/ro-journey.d.ts.map +1 -0
  437. package/dist/i18n/ro-journey.js +228 -0
  438. package/dist/i18n/ro-operations.d.ts +382 -0
  439. package/dist/i18n/ro-operations.d.ts.map +1 -0
  440. package/dist/i18n/ro-operations.js +381 -0
  441. package/dist/i18n/ro-sections.d.ts +360 -0
  442. package/dist/i18n/ro-sections.d.ts.map +1 -0
  443. package/dist/i18n/ro-sections.js +359 -0
  444. package/dist/i18n/ro.d.ts +1581 -0
  445. package/dist/i18n/ro.d.ts.map +1 -0
  446. package/dist/i18n/ro.js +12 -0
  447. package/dist/index.d.ts +9 -0
  448. package/dist/index.d.ts.map +1 -0
  449. package/dist/index.js +8 -0
  450. package/dist/journey/components/booking-journey-rules.d.ts +48 -0
  451. package/dist/journey/components/booking-journey-rules.d.ts.map +1 -0
  452. package/dist/journey/components/booking-journey-rules.js +235 -0
  453. package/dist/journey/components/booking-journey.d.ts +3 -0
  454. package/dist/journey/components/booking-journey.d.ts.map +1 -0
  455. package/dist/journey/components/booking-journey.js +368 -0
  456. package/dist/journey/components/configure-step-skeleton.d.ts +8 -0
  457. package/dist/journey/components/configure-step-skeleton.d.ts.map +1 -0
  458. package/dist/journey/components/configure-step-skeleton.js +11 -0
  459. package/dist/journey/components/contract-preview-dialog.d.ts +47 -0
  460. package/dist/journey/components/contract-preview-dialog.d.ts.map +1 -0
  461. package/dist/journey/components/contract-preview-dialog.js +124 -0
  462. package/dist/journey/components/journey-steps/accommodation-step.d.ts +3 -0
  463. package/dist/journey/components/journey-steps/accommodation-step.d.ts.map +1 -0
  464. package/dist/journey/components/journey-steps/accommodation-step.js +71 -0
  465. package/dist/journey/components/journey-steps/addons-step.d.ts +3 -0
  466. package/dist/journey/components/journey-steps/addons-step.d.ts.map +1 -0
  467. package/dist/journey/components/journey-steps/addons-step.js +40 -0
  468. package/dist/journey/components/journey-steps/billing-step.d.ts +8 -0
  469. package/dist/journey/components/journey-steps/billing-step.d.ts.map +1 -0
  470. package/dist/journey/components/journey-steps/billing-step.js +78 -0
  471. package/dist/journey/components/journey-steps/configure-steps.d.ts +28 -0
  472. package/dist/journey/components/journey-steps/configure-steps.d.ts.map +1 -0
  473. package/dist/journey/components/journey-steps/configure-steps.js +232 -0
  474. package/dist/journey/components/journey-steps/documents-step.d.ts +11 -0
  475. package/dist/journey/components/journey-steps/documents-step.d.ts.map +1 -0
  476. package/dist/journey/components/journey-steps/documents-step.js +36 -0
  477. package/dist/journey/components/journey-steps/payment-step.d.ts +29 -0
  478. package/dist/journey/components/journey-steps/payment-step.d.ts.map +1 -0
  479. package/dist/journey/components/journey-steps/payment-step.js +225 -0
  480. package/dist/journey/components/journey-steps/review-step.d.ts +27 -0
  481. package/dist/journey/components/journey-steps/review-step.d.ts.map +1 -0
  482. package/dist/journey/components/journey-steps/review-step.js +18 -0
  483. package/dist/journey/components/journey-steps/shared.d.ts +75 -0
  484. package/dist/journey/components/journey-steps/shared.d.ts.map +1 -0
  485. package/dist/journey/components/journey-steps/shared.js +108 -0
  486. package/dist/journey/components/journey-steps/travelers-step.d.ts +7 -0
  487. package/dist/journey/components/journey-steps/travelers-step.d.ts.map +1 -0
  488. package/dist/journey/components/journey-steps/travelers-step.js +201 -0
  489. package/dist/journey/components/journey-steps.d.ts +21 -0
  490. package/dist/journey/components/journey-steps.d.ts.map +1 -0
  491. package/dist/journey/components/journey-steps.js +20 -0
  492. package/dist/journey/components/side-panel.d.ts +17 -0
  493. package/dist/journey/components/side-panel.d.ts.map +1 -0
  494. package/dist/journey/components/side-panel.js +245 -0
  495. package/dist/journey/components/stacked-journey.d.ts +30 -0
  496. package/dist/journey/components/stacked-journey.d.ts.map +1 -0
  497. package/dist/journey/components/stacked-journey.js +50 -0
  498. package/dist/journey/components/step-header.d.ts +7 -0
  499. package/dist/journey/components/step-header.d.ts.map +1 -0
  500. package/dist/journey/components/step-header.js +12 -0
  501. package/dist/journey/index.d.ts +18 -0
  502. package/dist/journey/index.d.ts.map +1 -0
  503. package/dist/journey/index.js +17 -0
  504. package/dist/journey/lib/draft-state.d.ts +35 -0
  505. package/dist/journey/lib/draft-state.d.ts.map +1 -0
  506. package/dist/journey/lib/draft-state.js +57 -0
  507. package/dist/journey/lib/pax-band-dependencies.d.ts +27 -0
  508. package/dist/journey/lib/pax-band-dependencies.d.ts.map +1 -0
  509. package/dist/journey/lib/pax-band-dependencies.js +50 -0
  510. package/dist/journey/lib/payment-schedule.d.ts +19 -0
  511. package/dist/journey/lib/payment-schedule.d.ts.map +1 -0
  512. package/dist/journey/lib/payment-schedule.js +90 -0
  513. package/dist/journey/types.d.ts +403 -0
  514. package/dist/journey/types.d.ts.map +1 -0
  515. package/dist/journey/types.js +19 -0
  516. package/dist/provider.d.ts +2 -0
  517. package/dist/provider.d.ts.map +1 -0
  518. package/dist/provider.js +1 -0
  519. package/dist/query-keys.d.ts +74 -0
  520. package/dist/query-keys.d.ts.map +1 -0
  521. package/dist/query-keys.js +26 -0
  522. package/dist/query-options.d.ts +2534 -0
  523. package/dist/query-options.d.ts.map +1 -0
  524. package/dist/query-options.js +233 -0
  525. package/dist/requirements/client.d.ts +14 -0
  526. package/dist/requirements/client.d.ts.map +1 -0
  527. package/dist/requirements/client.js +59 -0
  528. package/dist/requirements/components/booking-requirements-contact-tab.d.ts +8 -0
  529. package/dist/requirements/components/booking-requirements-contact-tab.d.ts.map +1 -0
  530. package/dist/requirements/components/booking-requirements-contact-tab.js +8 -0
  531. package/dist/requirements/components/booking-requirements-questions-tab.d.ts +14 -0
  532. package/dist/requirements/components/booking-requirements-questions-tab.d.ts.map +1 -0
  533. package/dist/requirements/components/booking-requirements-questions-tab.js +17 -0
  534. package/dist/requirements/constants.d.ts +114 -0
  535. package/dist/requirements/constants.d.ts.map +1 -0
  536. package/dist/requirements/constants.js +45 -0
  537. package/dist/requirements/hooks/index.d.ts +6 -0
  538. package/dist/requirements/hooks/index.d.ts.map +1 -0
  539. package/dist/requirements/hooks/index.js +6 -0
  540. package/dist/requirements/hooks/use-booking-questions.d.ts +24 -0
  541. package/dist/requirements/hooks/use-booking-questions.d.ts.map +1 -0
  542. package/dist/requirements/hooks/use-booking-questions.js +9 -0
  543. package/dist/requirements/hooks/use-contact-requirements.d.ts +22 -0
  544. package/dist/requirements/hooks/use-contact-requirements.d.ts.map +1 -0
  545. package/dist/requirements/hooks/use-contact-requirements.js +9 -0
  546. package/dist/requirements/hooks/use-products.d.ts +16 -0
  547. package/dist/requirements/hooks/use-products.d.ts.map +1 -0
  548. package/dist/requirements/hooks/use-products.js +9 -0
  549. package/dist/requirements/hooks/use-question-options.d.ts +19 -0
  550. package/dist/requirements/hooks/use-question-options.d.ts.map +1 -0
  551. package/dist/requirements/hooks/use-question-options.js +9 -0
  552. package/dist/requirements/hooks/use-transport-requirements.d.ts +30 -0
  553. package/dist/requirements/hooks/use-transport-requirements.d.ts.map +1 -0
  554. package/dist/requirements/hooks/use-transport-requirements.js +9 -0
  555. package/dist/requirements/i18n/en.d.ts +94 -0
  556. package/dist/requirements/i18n/en.d.ts.map +1 -0
  557. package/dist/requirements/i18n/en.js +93 -0
  558. package/dist/requirements/i18n/index.d.ts +5 -0
  559. package/dist/requirements/i18n/index.d.ts.map +1 -0
  560. package/dist/requirements/i18n/index.js +3 -0
  561. package/dist/requirements/i18n/messages.d.ts +59 -0
  562. package/dist/requirements/i18n/messages.d.ts.map +1 -0
  563. package/dist/requirements/i18n/messages.js +1 -0
  564. package/dist/requirements/i18n/provider.d.ts +210 -0
  565. package/dist/requirements/i18n/provider.d.ts.map +1 -0
  566. package/dist/requirements/i18n/provider.js +44 -0
  567. package/dist/requirements/i18n/ro.d.ts +94 -0
  568. package/dist/requirements/i18n/ro.d.ts.map +1 -0
  569. package/dist/requirements/i18n/ro.js +93 -0
  570. package/dist/requirements/index.d.ts +9 -0
  571. package/dist/requirements/index.d.ts.map +1 -0
  572. package/dist/requirements/index.js +8 -0
  573. package/dist/requirements/provider.d.ts +2 -0
  574. package/dist/requirements/provider.d.ts.map +1 -0
  575. package/dist/requirements/provider.js +1 -0
  576. package/dist/requirements/query-keys.d.ts +33 -0
  577. package/dist/requirements/query-keys.d.ts.map +1 -0
  578. package/dist/requirements/query-keys.js +13 -0
  579. package/dist/requirements/query-options.d.ts +371 -0
  580. package/dist/requirements/query-options.d.ts.map +1 -0
  581. package/dist/requirements/query-options.js +80 -0
  582. package/dist/requirements/schemas.d.ts +320 -0
  583. package/dist/requirements/schemas.d.ts.map +1 -0
  584. package/dist/requirements/schemas.js +121 -0
  585. package/dist/requirements/ui.d.ts +4 -0
  586. package/dist/requirements/ui.d.ts.map +1 -0
  587. package/dist/requirements/ui.js +3 -0
  588. package/dist/requirements/utils.d.ts +2 -0
  589. package/dist/requirements/utils.d.ts.map +1 -0
  590. package/dist/requirements/utils.js +4 -0
  591. package/dist/schemas.d.ts +2070 -0
  592. package/dist/schemas.d.ts.map +1 -0
  593. package/dist/schemas.js +507 -0
  594. package/dist/status-presentation.d.ts +34 -0
  595. package/dist/status-presentation.d.ts.map +1 -0
  596. package/dist/status-presentation.js +38 -0
  597. package/dist/ui.d.ts +43 -0
  598. package/dist/ui.d.ts.map +1 -0
  599. package/dist/ui.js +42 -0
  600. package/package.json +256 -0
  601. package/src/requirements/styles.css +1 -0
  602. 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
+ }