@voyant-travel/catalog-react 0.117.2
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 +36 -0
- package/dist/admin/catalog-vertical-host.d.ts +45 -0
- package/dist/admin/catalog-vertical-host.d.ts.map +1 -0
- package/dist/admin/catalog-vertical-host.js +230 -0
- package/dist/admin/cruise-detail-host.d.ts +11 -0
- package/dist/admin/cruise-detail-host.d.ts.map +1 -0
- package/dist/admin/cruise-detail-host.js +33 -0
- package/dist/admin/dynamic-catalog-host.d.ts +13 -0
- package/dist/admin/dynamic-catalog-host.d.ts.map +1 -0
- package/dist/admin/dynamic-catalog-host.js +17 -0
- package/dist/admin/index.d.ts +133 -0
- package/dist/admin/index.d.ts.map +1 -0
- package/dist/admin/index.js +144 -0
- package/dist/admin/open-in-new-tab.d.ts +7 -0
- package/dist/admin/open-in-new-tab.d.ts.map +1 -0
- package/dist/admin/open-in-new-tab.js +10 -0
- package/dist/admin/pages/catalog-accommodations-detail-page.d.ts +4 -0
- package/dist/admin/pages/catalog-accommodations-detail-page.d.ts.map +1 -0
- package/dist/admin/pages/catalog-accommodations-detail-page.js +7 -0
- package/dist/admin/pages/catalog-accommodations-index-page.d.ts +8 -0
- package/dist/admin/pages/catalog-accommodations-index-page.d.ts.map +1 -0
- package/dist/admin/pages/catalog-accommodations-index-page.js +17 -0
- package/dist/admin/pages/catalog-cruises-detail-page.d.ts +4 -0
- package/dist/admin/pages/catalog-cruises-detail-page.d.ts.map +1 -0
- package/dist/admin/pages/catalog-cruises-detail-page.js +7 -0
- package/dist/admin/pages/catalog-cruises-index-page.d.ts +8 -0
- package/dist/admin/pages/catalog-cruises-index-page.d.ts.map +1 -0
- package/dist/admin/pages/catalog-cruises-index-page.js +19 -0
- package/dist/admin/pages/catalog-excursions-detail-page.d.ts +4 -0
- package/dist/admin/pages/catalog-excursions-detail-page.d.ts.map +1 -0
- package/dist/admin/pages/catalog-excursions-detail-page.js +7 -0
- package/dist/admin/pages/catalog-excursions-index-page.d.ts +8 -0
- package/dist/admin/pages/catalog-excursions-index-page.d.ts.map +1 -0
- package/dist/admin/pages/catalog-excursions-index-page.js +12 -0
- package/dist/admin/pages/catalog-products-detail-page.d.ts +8 -0
- package/dist/admin/pages/catalog-products-detail-page.d.ts.map +1 -0
- package/dist/admin/pages/catalog-products-detail-page.js +12 -0
- package/dist/admin/pages/catalog-products-index-page.d.ts +8 -0
- package/dist/admin/pages/catalog-products-index-page.d.ts.map +1 -0
- package/dist/admin/pages/catalog-products-index-page.js +12 -0
- package/dist/admin/pages/catalog-tours-detail-page.d.ts +4 -0
- package/dist/admin/pages/catalog-tours-detail-page.d.ts.map +1 -0
- package/dist/admin/pages/catalog-tours-detail-page.js +7 -0
- package/dist/admin/pages/catalog-tours-index-page.d.ts +8 -0
- package/dist/admin/pages/catalog-tours-index-page.d.ts.map +1 -0
- package/dist/admin/pages/catalog-tours-index-page.js +12 -0
- package/dist/admin/product-detail-host.d.ts +18 -0
- package/dist/admin/product-detail-host.d.ts.map +1 -0
- package/dist/admin/product-detail-host.js +40 -0
- package/dist/admin/scheduled-catalog-host.d.ts +15 -0
- package/dist/admin/scheduled-catalog-host.d.ts.map +1 -0
- package/dist/admin/scheduled-catalog-host.js +19 -0
- package/dist/admin/vertical-detail-host.d.ts +13 -0
- package/dist/admin/vertical-detail-host.d.ts.map +1 -0
- package/dist/admin/vertical-detail-host.js +62 -0
- package/dist/booking-engine/index.d.ts +26 -0
- package/dist/booking-engine/index.d.ts.map +1 -0
- package/dist/booking-engine/index.js +25 -0
- package/dist/booking-engine/use-booking-commit.d.ts +61 -0
- package/dist/booking-engine/use-booking-commit.d.ts.map +1 -0
- package/dist/booking-engine/use-booking-commit.js +47 -0
- package/dist/booking-engine/use-booking-draft-shape.d.ts +20 -0
- package/dist/booking-engine/use-booking-draft-shape.d.ts.map +1 -0
- package/dist/booking-engine/use-booking-draft-shape.js +9 -0
- package/dist/booking-engine/use-booking-draft.d.ts +102 -0
- package/dist/booking-engine/use-booking-draft.d.ts.map +1 -0
- package/dist/booking-engine/use-booking-draft.js +93 -0
- package/dist/booking-engine/use-booking-hold.d.ts +30 -0
- package/dist/booking-engine/use-booking-hold.d.ts.map +1 -0
- package/dist/booking-engine/use-booking-hold.js +44 -0
- package/dist/booking-engine/use-booking-journey-api.d.ts +23 -0
- package/dist/booking-engine/use-booking-journey-api.d.ts.map +1 -0
- package/dist/booking-engine/use-booking-journey-api.js +24 -0
- package/dist/booking-engine/use-booking-quote.d.ts +711 -0
- package/dist/booking-engine/use-booking-quote.d.ts.map +1 -0
- package/dist/booking-engine/use-booking-quote.js +122 -0
- package/dist/catalog-enrichment-mappers.d.ts +162 -0
- package/dist/catalog-enrichment-mappers.d.ts.map +1 -0
- package/dist/catalog-enrichment-mappers.js +190 -0
- package/dist/catalog-enrichment.d.ts +203 -0
- package/dist/catalog-enrichment.d.ts.map +1 -0
- package/dist/catalog-enrichment.js +130 -0
- package/dist/catalog-offers-client.d.ts +58 -0
- package/dist/catalog-offers-client.d.ts.map +1 -0
- package/dist/catalog-offers-client.js +61 -0
- package/dist/catalog-search-params.d.ts +45 -0
- package/dist/catalog-search-params.d.ts.map +1 -0
- package/dist/catalog-search-params.js +30 -0
- package/dist/catalog-surfaces.d.ts +17 -0
- package/dist/catalog-surfaces.d.ts.map +1 -0
- package/dist/catalog-surfaces.js +26 -0
- package/dist/client.d.ts +20 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +65 -0
- package/dist/components/availability-calendar.d.ts +33 -0
- package/dist/components/availability-calendar.d.ts.map +1 -0
- package/dist/components/availability-calendar.js +65 -0
- package/dist/components/catalog-browse-page.d.ts +41 -0
- package/dist/components/catalog-browse-page.d.ts.map +1 -0
- package/dist/components/catalog-browse-page.js +47 -0
- package/dist/components/catalog-card.d.ts +68 -0
- package/dist/components/catalog-card.d.ts.map +1 -0
- package/dist/components/catalog-card.js +52 -0
- package/dist/components/catalog-detail-cruise-cards.d.ts +16 -0
- package/dist/components/catalog-detail-cruise-cards.d.ts.map +1 -0
- package/dist/components/catalog-detail-cruise-cards.js +54 -0
- package/dist/components/catalog-detail-departures.d.ts +25 -0
- package/dist/components/catalog-detail-departures.d.ts.map +1 -0
- package/dist/components/catalog-detail-departures.js +240 -0
- package/dist/components/catalog-detail-parts.d.ts +70 -0
- package/dist/components/catalog-detail-parts.d.ts.map +1 -0
- package/dist/components/catalog-detail-parts.js +282 -0
- package/dist/components/catalog-detail-sheet.d.ts +93 -0
- package/dist/components/catalog-detail-sheet.d.ts.map +1 -0
- package/dist/components/catalog-detail-sheet.js +68 -0
- package/dist/components/catalog-detail-view.d.ts +39 -0
- package/dist/components/catalog-detail-view.d.ts.map +1 -0
- package/dist/components/catalog-detail-view.js +157 -0
- package/dist/components/catalog-enrichment-fetchers.d.ts +8 -0
- package/dist/components/catalog-enrichment-fetchers.d.ts.map +1 -0
- package/dist/components/catalog-enrichment-fetchers.js +7 -0
- package/dist/components/catalog-faceted-filter.d.ts +30 -0
- package/dist/components/catalog-faceted-filter.d.ts.map +1 -0
- package/dist/components/catalog-faceted-filter.js +24 -0
- package/dist/components/catalog-filter-rail.d.ts +25 -0
- package/dist/components/catalog-filter-rail.d.ts.map +1 -0
- package/dist/components/catalog-filter-rail.js +88 -0
- package/dist/components/catalog-gallery.d.ts +27 -0
- package/dist/components/catalog-gallery.d.ts.map +1 -0
- package/dist/components/catalog-gallery.js +66 -0
- package/dist/components/catalog-hit.d.ts +27 -0
- package/dist/components/catalog-hit.d.ts.map +1 -0
- package/dist/components/catalog-hit.js +57 -0
- package/dist/components/catalog-page-cards.d.ts +21 -0
- package/dist/components/catalog-page-cards.d.ts.map +1 -0
- package/dist/components/catalog-page-cards.js +174 -0
- package/dist/components/catalog-page-config.d.ts +17 -0
- package/dist/components/catalog-page-config.d.ts.map +1 -0
- package/dist/components/catalog-page-config.js +369 -0
- package/dist/components/catalog-page.d.ts +88 -0
- package/dist/components/catalog-page.d.ts.map +1 -0
- package/dist/components/catalog-page.js +148 -0
- package/dist/components/catalog-range-filter.d.ts +34 -0
- package/dist/components/catalog-range-filter.d.ts.map +1 -0
- package/dist/components/catalog-range-filter.js +72 -0
- package/dist/components/catalog-search-page.d.ts +239 -0
- package/dist/components/catalog-search-page.d.ts.map +1 -0
- package/dist/components/catalog-search-page.js +63 -0
- package/dist/components/catalog-search-tab-panel.d.ts +42 -0
- package/dist/components/catalog-search-tab-panel.d.ts.map +1 -0
- package/dist/components/catalog-search-tab-panel.js +199 -0
- package/dist/components/catalog-vertical-detail-page.d.ts +33 -0
- package/dist/components/catalog-vertical-detail-page.d.ts.map +1 -0
- package/dist/components/catalog-vertical-detail-page.js +100 -0
- package/dist/components/cruise-detail-page-parts.d.ts +72 -0
- package/dist/components/cruise-detail-page-parts.d.ts.map +1 -0
- package/dist/components/cruise-detail-page-parts.js +146 -0
- package/dist/components/cruise-detail-page.d.ts +21 -0
- package/dist/components/cruise-detail-page.d.ts.map +1 -0
- package/dist/components/cruise-detail-page.js +201 -0
- package/dist/components/dynamic-catalog-page-parts.d.ts +40 -0
- package/dist/components/dynamic-catalog-page-parts.d.ts.map +1 -0
- package/dist/components/dynamic-catalog-page-parts.js +43 -0
- package/dist/components/dynamic-catalog-page.d.ts +23 -0
- package/dist/components/dynamic-catalog-page.d.ts.map +1 -0
- package/dist/components/dynamic-catalog-page.js +270 -0
- package/dist/components/media-gallery.d.ts +13 -0
- package/dist/components/media-gallery.d.ts.map +1 -0
- package/dist/components/media-gallery.js +42 -0
- package/dist/components/product-detail-page-parts.d.ts +106 -0
- package/dist/components/product-detail-page-parts.d.ts.map +1 -0
- package/dist/components/product-detail-page-parts.js +130 -0
- package/dist/components/product-detail-page.d.ts +57 -0
- package/dist/components/product-detail-page.d.ts.map +1 -0
- package/dist/components/product-detail-page.js +175 -0
- package/dist/components/scheduled-catalog-page.d.ts +34 -0
- package/dist/components/scheduled-catalog-page.d.ts.map +1 -0
- package/dist/components/scheduled-catalog-page.js +6 -0
- package/dist/hooks/index.d.ts +3 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +2 -0
- package/dist/hooks/use-catalog-offers.d.ts +186 -0
- package/dist/hooks/use-catalog-offers.d.ts.map +1 -0
- package/dist/hooks/use-catalog-offers.js +105 -0
- package/dist/hooks/use-catalog-search.d.ts +109 -0
- package/dist/hooks/use-catalog-search.d.ts.map +1 -0
- package/dist/hooks/use-catalog-search.js +52 -0
- package/dist/i18n/en.d.ts +397 -0
- package/dist/i18n/en.d.ts.map +1 -0
- package/dist/i18n/en.js +396 -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.d.ts +335 -0
- package/dist/i18n/messages.d.ts.map +1 -0
- package/dist/i18n/messages.js +1 -0
- package/dist/i18n/provider.d.ts +816 -0
- package/dist/i18n/provider.d.ts.map +1 -0
- package/dist/i18n/provider.js +44 -0
- package/dist/i18n/ro.d.ts +397 -0
- package/dist/i18n/ro.d.ts.map +1 -0
- package/dist/i18n/ro.js +396 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/provider.d.ts +2 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/provider.js +1 -0
- package/dist/schemas-catalog-offers.d.ts +290 -0
- package/dist/schemas-catalog-offers.d.ts.map +1 -0
- package/dist/schemas-catalog-offers.js +155 -0
- package/dist/schemas.d.ts +143 -0
- package/dist/schemas.d.ts.map +1 -0
- package/dist/schemas.js +76 -0
- package/dist/ui.d.ts +19 -0
- package/dist/ui.d.ts.map +1 -0
- package/dist/ui.js +18 -0
- package/package.json +150 -0
- package/src/styles.css +11 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type ScheduledScope } from "../components/scheduled-catalog-page.js";
|
|
2
|
+
import type { CatalogSearchParams } from "../index.js";
|
|
3
|
+
export interface ScheduledCatalogHostProps {
|
|
4
|
+
scope: ScheduledScope;
|
|
5
|
+
search: CatalogSearchParams;
|
|
6
|
+
onSearchChange: (updater: (prev: CatalogSearchParams) => CatalogSearchParams, replace?: boolean) => void;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Packaged admin host for `ScheduledCatalogPage` — supplies the localized
|
|
10
|
+
* title/tagline and renders `CatalogVerticalHost` (markets/suppliers/products
|
|
11
|
+
* wiring) as the embedded browse grid, opening each result's vertical detail
|
|
12
|
+
* page (the `catalog.detail` destination) in a new tab on click.
|
|
13
|
+
*/
|
|
14
|
+
export declare function ScheduledCatalogHost({ scope, search, onSearchChange }: ScheduledCatalogHostProps): import("react/jsx-runtime").JSX.Element;
|
|
15
|
+
//# sourceMappingURL=scheduled-catalog-host.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduled-catalog-host.d.ts","sourceRoot":"","sources":["../../src/admin/scheduled-catalog-host.tsx"],"names":[],"mappings":"AAGA,OAAO,EAEL,KAAK,cAAc,EACpB,MAAM,yCAAyC,CAAA;AAChD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAA;AAItD,MAAM,WAAW,yBAAyB;IACxC,KAAK,EAAE,cAAc,CAAA;IACrB,MAAM,EAAE,mBAAmB,CAAA;IAC3B,cAAc,EAAE,CACd,OAAO,EAAE,CAAC,IAAI,EAAE,mBAAmB,KAAK,mBAAmB,EAC3D,OAAO,CAAC,EAAE,OAAO,KACd,IAAI,CAAA;CACV;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,EAAE,yBAAyB,2CA0BhG"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useAdminHref, useOperatorAdminMessages } from "@voyant-travel/admin";
|
|
4
|
+
import { ScheduledCatalogPage as ScheduledCatalogPageUi, } from "../components/scheduled-catalog-page.js";
|
|
5
|
+
import { CatalogVerticalHost } from "./catalog-vertical-host.js";
|
|
6
|
+
import { openHrefInNewTab } from "./open-in-new-tab.js";
|
|
7
|
+
/**
|
|
8
|
+
* Packaged admin host for `ScheduledCatalogPage` — supplies the localized
|
|
9
|
+
* title/tagline and renders `CatalogVerticalHost` (markets/suppliers/products
|
|
10
|
+
* wiring) as the embedded browse grid, opening each result's vertical detail
|
|
11
|
+
* page (the `catalog.detail` destination) in a new tab on click.
|
|
12
|
+
*/
|
|
13
|
+
export function ScheduledCatalogHost({ scope, search, onSearchChange }) {
|
|
14
|
+
const resolveHref = useAdminHref();
|
|
15
|
+
const nav = useOperatorAdminMessages().nav;
|
|
16
|
+
const title = scope === "excursions" ? nav.catalogExcursions : nav.catalogTours;
|
|
17
|
+
const subtitle = scope === "excursions" ? nav.catalogExcursionsTagline : nav.catalogToursTagline;
|
|
18
|
+
return (_jsx(ScheduledCatalogPageUi, { scope: scope, title: title, subtitle: subtitle, renderBrowseGrid: ({ lockedFacets, lockedRanges }) => (_jsx(CatalogVerticalHost, { vertical: "products", search: search, onSearchChange: onSearchChange, embedded: true, lockedFacets: lockedFacets, lockedRanges: lockedRanges, onOpenDetail: (hit) => openHrefInNewTab(resolveHref("catalog.detail", { surface: scope, id: hit.id })) })) }));
|
|
19
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type CatalogDetailSurface } from "../catalog-surfaces.js";
|
|
2
|
+
export interface VerticalDetailHostProps {
|
|
3
|
+
surface: CatalogDetailSurface;
|
|
4
|
+
id: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Packaged admin host for `CatalogVerticalDetailPage` — binds the supplier
|
|
8
|
+
* directory, the localized surface label, semantic-destination navigation
|
|
9
|
+
* into the booking journey (packaged-admin RFC §4.7), and breadcrumbs, so
|
|
10
|
+
* host route files stay trivial.
|
|
11
|
+
*/
|
|
12
|
+
export declare function VerticalDetailHost({ surface, id }: VerticalDetailHostProps): import("react/jsx-runtime").JSX.Element;
|
|
13
|
+
//# sourceMappingURL=vertical-detail-host.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vertical-detail-host.d.ts","sourceRoot":"","sources":["../../src/admin/vertical-detail-host.tsx"],"names":[],"mappings":"AAWA,OAAO,EAAE,KAAK,oBAAoB,EAA0B,MAAM,wBAAwB,CAAA;AAM1F,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,oBAAoB,CAAA;IAC7B,EAAE,EAAE,MAAM,CAAA;CACX;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,uBAAuB,2CA+C1E"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useAdminBreadcrumbs, useAdminHref, useAdminNavigate, useOperatorAdminMessages, } from "@voyant-travel/admin";
|
|
4
|
+
import { useSuppliers } from "@voyant-travel/distribution-react/suppliers";
|
|
5
|
+
import { useMemo, useState } from "react";
|
|
6
|
+
import { catalogSurfaceVertical } from "../catalog-surfaces.js";
|
|
7
|
+
import { CatalogVerticalDetailPage, } from "../components/catalog-vertical-detail-page.js";
|
|
8
|
+
/**
|
|
9
|
+
* Packaged admin host for `CatalogVerticalDetailPage` — binds the supplier
|
|
10
|
+
* directory, the localized surface label, semantic-destination navigation
|
|
11
|
+
* into the booking journey (packaged-admin RFC §4.7), and breadcrumbs, so
|
|
12
|
+
* host route files stay trivial.
|
|
13
|
+
*/
|
|
14
|
+
export function VerticalDetailHost({ surface, id }) {
|
|
15
|
+
const resolveHref = useAdminHref();
|
|
16
|
+
const navigateTo = useAdminNavigate();
|
|
17
|
+
const nav = useOperatorAdminMessages().nav;
|
|
18
|
+
const vertical = catalogSurfaceVertical(surface);
|
|
19
|
+
const surfaceLabel = surfaceTitle(surface, nav);
|
|
20
|
+
const surfaceHref = resolveHref("catalog.browse", { surface });
|
|
21
|
+
const suppliersQuery = useSuppliers({ limit: 100 });
|
|
22
|
+
const formatSupplier = useMemo(() => {
|
|
23
|
+
const m = new Map();
|
|
24
|
+
for (const sup of suppliersQuery.data?.data ?? [])
|
|
25
|
+
m.set(sup.id, sup.name);
|
|
26
|
+
return (sid) => m.get(String(sid)) ?? String(sid);
|
|
27
|
+
}, [suppliersQuery.data]);
|
|
28
|
+
// The packaged page resolves the title from content; mirror its breadcrumbs
|
|
29
|
+
// into the admin breadcrumb bar.
|
|
30
|
+
const [crumbs, setCrumbs] = useState([
|
|
31
|
+
{ label: surfaceLabel, href: surfaceHref },
|
|
32
|
+
]);
|
|
33
|
+
useAdminBreadcrumbs(crumbs);
|
|
34
|
+
return (_jsx(CatalogVerticalDetailPage, { id: id, vertical: vertical, surfaceLabel: surfaceLabel, surfaceHref: surfaceHref, formatSupplier: formatSupplier, onBreadcrumbs: setCrumbs,
|
|
35
|
+
// Connect-sourced vertical (accommodation/excursion/tour) → the unified
|
|
36
|
+
// journey. The picked departure's date locks the departure step; name/hero
|
|
37
|
+
// preview the panel.
|
|
38
|
+
onBook: (entityModule, entityId, opts) => navigateTo("bookingJourney.start", {
|
|
39
|
+
entityModule,
|
|
40
|
+
entityId,
|
|
41
|
+
sourceKind: "voyant-connect",
|
|
42
|
+
...(opts.departureId ? { departureId: opts.departureId } : {}),
|
|
43
|
+
...(opts.departureDate ? { departureDate: opts.departureDate.slice(0, 10) } : {}),
|
|
44
|
+
...(opts.optionId ? { optionId: opts.optionId } : {}),
|
|
45
|
+
...(opts.name ? { entityName: opts.name } : {}),
|
|
46
|
+
...(opts.heroImageUrl ? { entityImageUrl: opts.heroImageUrl } : {}),
|
|
47
|
+
}) }));
|
|
48
|
+
}
|
|
49
|
+
function surfaceTitle(surface, nav) {
|
|
50
|
+
switch (surface) {
|
|
51
|
+
case "cruises":
|
|
52
|
+
return nav.catalogCruises;
|
|
53
|
+
case "accommodations":
|
|
54
|
+
return nav.catalogAccommodations;
|
|
55
|
+
case "excursions":
|
|
56
|
+
return nav.catalogExcursions;
|
|
57
|
+
case "tours":
|
|
58
|
+
return nav.catalogTours;
|
|
59
|
+
default:
|
|
60
|
+
return nav.catalogProducts;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@voyant-travel/catalog-react/booking-engine` — React Query hooks
|
|
3
|
+
* driving the unified booking journey shell.
|
|
4
|
+
*
|
|
5
|
+
* Per `docs/architecture/booking-journey-architecture.md` §8.1.
|
|
6
|
+
*
|
|
7
|
+
* Hooks:
|
|
8
|
+
* - `useBookingDraft` — local draft state with server sync
|
|
9
|
+
* - `useBookingQuote` — debounced live quote on draft change
|
|
10
|
+
* - `useBookingDraftShape` — convenience accessor on the latest quote
|
|
11
|
+
* - `useBookingCommit` — final book mutation, triggers handoff
|
|
12
|
+
*
|
|
13
|
+
* The hooks share a `useBookingJourneyApi()` accessor that derives
|
|
14
|
+
* the API base from the surrounding `VoyantCatalogProvider` and
|
|
15
|
+
* lets callers override the surface (`/v1/admin` vs `/v1/public`).
|
|
16
|
+
*
|
|
17
|
+
* Identical surface for operator and storefront — Phase B's "build
|
|
18
|
+
* once, ship everywhere" rule (§ Rule 4).
|
|
19
|
+
*/
|
|
20
|
+
export { type UseBookingCommitOptions, useBookingCommit, } from "./use-booking-commit.js";
|
|
21
|
+
export { type UseBookingDraftOptions, useBookingDraft, } from "./use-booking-draft.js";
|
|
22
|
+
export { type UseBookingDraftShapeOptions, useBookingDraftShape, } from "./use-booking-draft-shape.js";
|
|
23
|
+
export { type PlaceHoldInput, type ReleaseHoldInput, useBookingHold, } from "./use-booking-hold.js";
|
|
24
|
+
export { type BookingJourneyApiOptions, type UseBookingJourneyApi, useBookingJourneyApi, } from "./use-booking-journey-api.js";
|
|
25
|
+
export { type UseBookingQuoteOptions, useBookingQuote, } from "./use-booking-quote.js";
|
|
26
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/booking-engine/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EACL,KAAK,uBAAuB,EAC5B,gBAAgB,GACjB,MAAM,yBAAyB,CAAA;AAChC,OAAO,EACL,KAAK,sBAAsB,EAC3B,eAAe,GAChB,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EACL,KAAK,2BAA2B,EAChC,oBAAoB,GACrB,MAAM,8BAA8B,CAAA;AACrC,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,cAAc,GACf,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EACL,KAAK,wBAAwB,EAC7B,KAAK,oBAAoB,EACzB,oBAAoB,GACrB,MAAM,8BAA8B,CAAA;AACrC,OAAO,EACL,KAAK,sBAAsB,EAC3B,eAAe,GAChB,MAAM,wBAAwB,CAAA"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@voyant-travel/catalog-react/booking-engine` — React Query hooks
|
|
3
|
+
* driving the unified booking journey shell.
|
|
4
|
+
*
|
|
5
|
+
* Per `docs/architecture/booking-journey-architecture.md` §8.1.
|
|
6
|
+
*
|
|
7
|
+
* Hooks:
|
|
8
|
+
* - `useBookingDraft` — local draft state with server sync
|
|
9
|
+
* - `useBookingQuote` — debounced live quote on draft change
|
|
10
|
+
* - `useBookingDraftShape` — convenience accessor on the latest quote
|
|
11
|
+
* - `useBookingCommit` — final book mutation, triggers handoff
|
|
12
|
+
*
|
|
13
|
+
* The hooks share a `useBookingJourneyApi()` accessor that derives
|
|
14
|
+
* the API base from the surrounding `VoyantCatalogProvider` and
|
|
15
|
+
* lets callers override the surface (`/v1/admin` vs `/v1/public`).
|
|
16
|
+
*
|
|
17
|
+
* Identical surface for operator and storefront — Phase B's "build
|
|
18
|
+
* once, ship everywhere" rule (§ Rule 4).
|
|
19
|
+
*/
|
|
20
|
+
export { useBookingCommit, } from "./use-booking-commit.js";
|
|
21
|
+
export { useBookingDraft, } from "./use-booking-draft.js";
|
|
22
|
+
export { useBookingDraftShape, } from "./use-booking-draft-shape.js";
|
|
23
|
+
export { useBookingHold, } from "./use-booking-hold.js";
|
|
24
|
+
export { useBookingJourneyApi, } from "./use-booking-journey-api.js";
|
|
25
|
+
export { useBookingQuote, } from "./use-booking-quote.js";
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { type BookingDraftV1, type BookingPaymentIntent, type BookResponseV1 } from "@voyant-travel/catalog-contracts/booking-engine/contracts";
|
|
2
|
+
import { type BookingJourneyApiOptions } from "./use-booking-journey-api.js";
|
|
3
|
+
export interface UseBookingCommitOptions extends BookingJourneyApiOptions {
|
|
4
|
+
/** Draft id — when set, the engine resolves the quote off the draft
|
|
5
|
+
* and the draft is marked consumed on success. */
|
|
6
|
+
draftId?: string;
|
|
7
|
+
/** Optional callback fired on successful commit. Templates use this
|
|
8
|
+
* for post-commit navigation (operator → /orders, storefront →
|
|
9
|
+
* /confirmation). */
|
|
10
|
+
onCommitted?: (result: BookResponseV1) => void;
|
|
11
|
+
}
|
|
12
|
+
export interface CommitInput {
|
|
13
|
+
/** Either-or: pass the draft for context-aware commit, or just a
|
|
14
|
+
* quoteId when the journey hasn't materialized a draft yet. */
|
|
15
|
+
draft?: BookingDraftV1;
|
|
16
|
+
quoteId?: string;
|
|
17
|
+
party?: Record<string, unknown>;
|
|
18
|
+
paymentIntent?: BookingPaymentIntent;
|
|
19
|
+
/** Initial booking status the owned handler should land on (draft /
|
|
20
|
+
* awaiting_payment / confirmed). When omitted, the create defaults to draft. */
|
|
21
|
+
initialStatus?: string;
|
|
22
|
+
/** Idempotency key — same key in 24h returns the existing booking. */
|
|
23
|
+
idempotencyKey?: string;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Final book mutation — calls `POST /catalog/book` with the chosen
|
|
27
|
+
* quoteId or draftId. The wizard's Review step calls this when the
|
|
28
|
+
* user clicks Confirm. Per booking-journey-architecture §8 + §10
|
|
29
|
+
* Phase B.
|
|
30
|
+
*/
|
|
31
|
+
export declare function useBookingCommit(options?: UseBookingCommitOptions): import("@tanstack/react-query").UseMutationResult<{
|
|
32
|
+
bookingId: string;
|
|
33
|
+
orderRef: string;
|
|
34
|
+
status: "failed" | "confirmed" | "held" | "ticketed";
|
|
35
|
+
snapshotId: string;
|
|
36
|
+
pricing?: {
|
|
37
|
+
currency: string;
|
|
38
|
+
lines: {
|
|
39
|
+
kind: "base" | "accommodation" | "fee" | "discount" | "addon" | "supplement";
|
|
40
|
+
label: string;
|
|
41
|
+
unitAmount: number;
|
|
42
|
+
totalAmount: number;
|
|
43
|
+
quantity?: number | undefined;
|
|
44
|
+
taxIncluded?: boolean | undefined;
|
|
45
|
+
}[];
|
|
46
|
+
taxes: {
|
|
47
|
+
code: string;
|
|
48
|
+
label: string;
|
|
49
|
+
rate: number;
|
|
50
|
+
amount: number;
|
|
51
|
+
base: number;
|
|
52
|
+
includedInPrice?: boolean | undefined;
|
|
53
|
+
scope?: "included" | "excluded" | "withheld" | undefined;
|
|
54
|
+
}[];
|
|
55
|
+
subtotal: number;
|
|
56
|
+
taxTotal: number;
|
|
57
|
+
total: number;
|
|
58
|
+
} | undefined;
|
|
59
|
+
upstreamPayload?: Record<string, unknown> | undefined;
|
|
60
|
+
}, Error, CommitInput, unknown>;
|
|
61
|
+
//# sourceMappingURL=use-booking-commit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-booking-commit.d.ts","sourceRoot":"","sources":["../../src/booking-engine/use-booking-commit.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,KAAK,cAAc,EACnB,KAAK,oBAAoB,EACzB,KAAK,cAAc,EAEpB,MAAM,2DAA2D,CAAA;AAElE,OAAO,EAAE,KAAK,wBAAwB,EAAwB,MAAM,8BAA8B,CAAA;AAElG,MAAM,WAAW,uBAAwB,SAAQ,wBAAwB;IACvE;uDACmD;IACnD,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;0BAEsB;IACtB,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,IAAI,CAAA;CAC/C;AAED,MAAM,WAAW,WAAW;IAC1B;oEACgE;IAChE,KAAK,CAAC,EAAE,cAAc,CAAA;IACtB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC/B,aAAa,CAAC,EAAE,oBAAoB,CAAA;IACpC;qFACiF;IACjF,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,sEAAsE;IACtE,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,GAAE,uBAA4B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gCA+BrE"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useMutation } from "@tanstack/react-query";
|
|
3
|
+
import { bookResponseV1, } from "@voyant-travel/catalog-contracts/booking-engine/contracts";
|
|
4
|
+
import { useBookingJourneyApi } from "./use-booking-journey-api.js";
|
|
5
|
+
/**
|
|
6
|
+
* Final book mutation — calls `POST /catalog/book` with the chosen
|
|
7
|
+
* quoteId or draftId. The wizard's Review step calls this when the
|
|
8
|
+
* user clicks Confirm. Per booking-journey-architecture §8 + §10
|
|
9
|
+
* Phase B.
|
|
10
|
+
*/
|
|
11
|
+
export function useBookingCommit(options = {}) {
|
|
12
|
+
const api = useBookingJourneyApi(options);
|
|
13
|
+
return useMutation({
|
|
14
|
+
mutationFn: async (input) => {
|
|
15
|
+
// Auto-generate an idempotency key when the caller didn't pass
|
|
16
|
+
// one — protects against double-clicks. The key is stable for
|
|
17
|
+
// the lifetime of the mutation function call (one user click =
|
|
18
|
+
// one key); a manual retry from the journey shell uses the same
|
|
19
|
+
// key by passing it explicitly.
|
|
20
|
+
const idempotencyKey = input.idempotencyKey ?? generateIdempotencyKey(input.draft?.entity?.id ?? input.quoteId);
|
|
21
|
+
const body = {
|
|
22
|
+
quoteId: input.quoteId ?? input.draft?.quoteId,
|
|
23
|
+
draftId: options.draftId,
|
|
24
|
+
party: input.party,
|
|
25
|
+
paymentIntent: input.paymentIntent,
|
|
26
|
+
idempotencyKey,
|
|
27
|
+
};
|
|
28
|
+
if (input.draft || input.initialStatus) {
|
|
29
|
+
body.parameters = {
|
|
30
|
+
...(input.draft ? { draft: input.draft } : {}),
|
|
31
|
+
...(input.initialStatus ? { initialStatus: input.initialStatus } : {}),
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
return api.request("POST", "/book", bookResponseV1, body);
|
|
35
|
+
},
|
|
36
|
+
onSuccess(result) {
|
|
37
|
+
options.onCommitted?.(result);
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
function generateIdempotencyKey(seed) {
|
|
42
|
+
const base = seed ?? "anon";
|
|
43
|
+
if (typeof globalThis.crypto !== "undefined" && globalThis.crypto.randomUUID) {
|
|
44
|
+
return `${base.slice(0, 32)}_${globalThis.crypto.randomUUID().replace(/-/g, "")}`;
|
|
45
|
+
}
|
|
46
|
+
return `${base.slice(0, 32)}_${Date.now().toString(36)}${Math.random().toString(36).slice(2, 12)}`;
|
|
47
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { BookingDraftShape } from "@voyant-travel/catalog-contracts/booking-engine/draft-shape";
|
|
2
|
+
import type { BookingJourneyApiOptions } from "./use-booking-journey-api.js";
|
|
3
|
+
export interface UseBookingDraftShapeOptions extends BookingJourneyApiOptions {
|
|
4
|
+
/** Quote response — typically passed in from the wrapping
|
|
5
|
+
* `useBookingQuote()`. */
|
|
6
|
+
quote: {
|
|
7
|
+
shape?: BookingDraftShape;
|
|
8
|
+
} | null;
|
|
9
|
+
/** Fallback shape rendered when the quote hasn't loaded yet — the
|
|
10
|
+
* journey shell uses this so the wizard renders an empty Configure
|
|
11
|
+
* step on first paint. */
|
|
12
|
+
fallback: BookingDraftShape;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Convenience accessor that returns the descriptor a quote response
|
|
16
|
+
* carries — or a caller-supplied fallback while we wait for the
|
|
17
|
+
* first quote. Per booking-journey-architecture §3 + §8.1.
|
|
18
|
+
*/
|
|
19
|
+
export declare function useBookingDraftShape(options: UseBookingDraftShapeOptions): BookingDraftShape;
|
|
20
|
+
//# sourceMappingURL=use-booking-draft-shape.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-booking-draft-shape.d.ts","sourceRoot":"","sources":["../../src/booking-engine/use-booking-draft-shape.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,6DAA6D,CAAA;AAEpG,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAA;AAE5E,MAAM,WAAW,2BAA4B,SAAQ,wBAAwB;IAC3E;+BAC2B;IAC3B,KAAK,EAAE;QAAE,KAAK,CAAC,EAAE,iBAAiB,CAAA;KAAE,GAAG,IAAI,CAAA;IAC3C;;+BAE2B;IAC3B,QAAQ,EAAE,iBAAiB,CAAA;CAC5B;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,2BAA2B,GAAG,iBAAiB,CAE5F"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
/**
|
|
3
|
+
* Convenience accessor that returns the descriptor a quote response
|
|
4
|
+
* carries — or a caller-supplied fallback while we wait for the
|
|
5
|
+
* first quote. Per booking-journey-architecture §3 + §8.1.
|
|
6
|
+
*/
|
|
7
|
+
export function useBookingDraftShape(options) {
|
|
8
|
+
return options.quote?.shape ?? options.fallback;
|
|
9
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { type BookingDraftV1 } from "@voyant-travel/catalog-contracts/booking-engine/contracts";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { type BookingJourneyApiOptions } from "./use-booking-journey-api.js";
|
|
4
|
+
declare const draftRowSchema: z.ZodObject<{
|
|
5
|
+
id: z.ZodString;
|
|
6
|
+
entity_module: z.ZodString;
|
|
7
|
+
entity_id: z.ZodString;
|
|
8
|
+
source_kind: z.ZodString;
|
|
9
|
+
source_connection_id: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
10
|
+
source_ref: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
11
|
+
draft_payload: z.ZodUnknown;
|
|
12
|
+
current_step: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
13
|
+
current_quote_id: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
14
|
+
hold_expires_at: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
15
|
+
consumed_booking_id: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
16
|
+
consumed_at: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
17
|
+
expires_at: z.ZodString;
|
|
18
|
+
created_at: z.ZodString;
|
|
19
|
+
updated_at: z.ZodString;
|
|
20
|
+
}, z.core.$strip>;
|
|
21
|
+
export type DraftRow = z.infer<typeof draftRowSchema>;
|
|
22
|
+
export interface UseBookingDraftOptions extends BookingJourneyApiOptions {
|
|
23
|
+
/** Stable draft id — caller generates and persists in URL or
|
|
24
|
+
* sessionStorage. */
|
|
25
|
+
draftId: string;
|
|
26
|
+
/** Initial entity pointer, required on first PUT. */
|
|
27
|
+
initialDraft?: BookingDraftV1;
|
|
28
|
+
/** Disable the GET — useful when the draft was just minted client-side. */
|
|
29
|
+
enableLoad?: boolean;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Server-synced booking draft state. Calls GET on mount (when
|
|
33
|
+
* `enableLoad`) and exposes a `save(draft)` mutation that PUTs
|
|
34
|
+
* to `/catalog/drafts/:id`.
|
|
35
|
+
*
|
|
36
|
+
* The mutation is fire-and-forget from the wizard's perspective —
|
|
37
|
+
* the journey root holds the local draft as the source of truth and
|
|
38
|
+
* the server copy is a recovery surface. Per
|
|
39
|
+
* booking-journey-architecture §5.7.
|
|
40
|
+
*/
|
|
41
|
+
export declare function useBookingDraft(options: UseBookingDraftOptions): {
|
|
42
|
+
/** Server-side draft row, or null when missing / 404. */
|
|
43
|
+
serverDraft: {
|
|
44
|
+
id: string;
|
|
45
|
+
entity_module: string;
|
|
46
|
+
entity_id: string;
|
|
47
|
+
source_kind: string;
|
|
48
|
+
draft_payload: unknown;
|
|
49
|
+
expires_at: string;
|
|
50
|
+
created_at: string;
|
|
51
|
+
updated_at: string;
|
|
52
|
+
source_connection_id?: string | null | undefined;
|
|
53
|
+
source_ref?: string | null | undefined;
|
|
54
|
+
current_step?: string | null | undefined;
|
|
55
|
+
current_quote_id?: string | null | undefined;
|
|
56
|
+
hold_expires_at?: string | null | undefined;
|
|
57
|
+
consumed_booking_id?: string | null | undefined;
|
|
58
|
+
consumed_at?: string | null | undefined;
|
|
59
|
+
} | null;
|
|
60
|
+
isLoading: boolean;
|
|
61
|
+
save: import("@tanstack/react-query").UseMutationResult<{
|
|
62
|
+
id: string;
|
|
63
|
+
entity_module: string;
|
|
64
|
+
entity_id: string;
|
|
65
|
+
source_kind: string;
|
|
66
|
+
draft_payload: unknown;
|
|
67
|
+
expires_at: string;
|
|
68
|
+
created_at: string;
|
|
69
|
+
updated_at: string;
|
|
70
|
+
source_connection_id?: string | null | undefined;
|
|
71
|
+
source_ref?: string | null | undefined;
|
|
72
|
+
current_step?: string | null | undefined;
|
|
73
|
+
current_quote_id?: string | null | undefined;
|
|
74
|
+
hold_expires_at?: string | null | undefined;
|
|
75
|
+
consumed_booking_id?: string | null | undefined;
|
|
76
|
+
consumed_at?: string | null | undefined;
|
|
77
|
+
}, Error, {
|
|
78
|
+
draft: BookingDraftV1;
|
|
79
|
+
currentStep?: string;
|
|
80
|
+
currentQuoteId?: string;
|
|
81
|
+
}, unknown>;
|
|
82
|
+
remove: import("@tanstack/react-query").UseMutationResult<void, Error, void, unknown>;
|
|
83
|
+
refetch: (options?: import("@tanstack/react-query").RefetchOptions) => Promise<import("@tanstack/react-query").QueryObserverResult<{
|
|
84
|
+
id: string;
|
|
85
|
+
entity_module: string;
|
|
86
|
+
entity_id: string;
|
|
87
|
+
source_kind: string;
|
|
88
|
+
draft_payload: unknown;
|
|
89
|
+
expires_at: string;
|
|
90
|
+
created_at: string;
|
|
91
|
+
updated_at: string;
|
|
92
|
+
source_connection_id?: string | null | undefined;
|
|
93
|
+
source_ref?: string | null | undefined;
|
|
94
|
+
current_step?: string | null | undefined;
|
|
95
|
+
current_quote_id?: string | null | undefined;
|
|
96
|
+
hold_expires_at?: string | null | undefined;
|
|
97
|
+
consumed_booking_id?: string | null | undefined;
|
|
98
|
+
consumed_at?: string | null | undefined;
|
|
99
|
+
} | null, Error>>;
|
|
100
|
+
};
|
|
101
|
+
export {};
|
|
102
|
+
//# sourceMappingURL=use-booking-draft.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-booking-draft.d.ts","sourceRoot":"","sources":["../../src/booking-engine/use-booking-draft.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,KAAK,cAAc,EAEpB,MAAM,2DAA2D,CAAA;AAClE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,EAAE,KAAK,wBAAwB,EAAwB,MAAM,8BAA8B,CAAA;AAElG,QAAA,MAAM,cAAc;;;;;;;;;;;;;;;;iBAgBlB,CAAA;AAEF,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAA;AAErD,MAAM,WAAW,sBAAuB,SAAQ,wBAAwB;IACtE;0BACsB;IACtB,OAAO,EAAE,MAAM,CAAA;IACf,qDAAqD;IACrD,YAAY,CAAC,EAAE,cAAc,CAAA;IAC7B,2EAA2E;IAC3E,UAAU,CAAC,EAAE,OAAO,CAAA;CACrB;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,sBAAsB;IA4D3D,yDAAyD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;eAtChD,cAAc;sBACP,MAAM;yBACH,MAAM;;;;;;;;;;;;;;;;;;;;EA2C5B"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
3
|
+
import { bookingDraftV1, } from "@voyant-travel/catalog-contracts/booking-engine/contracts";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { useBookingJourneyApi } from "./use-booking-journey-api.js";
|
|
6
|
+
const draftRowSchema = z.object({
|
|
7
|
+
id: z.string(),
|
|
8
|
+
entity_module: z.string(),
|
|
9
|
+
entity_id: z.string(),
|
|
10
|
+
source_kind: z.string(),
|
|
11
|
+
source_connection_id: z.string().nullable().optional(),
|
|
12
|
+
source_ref: z.string().nullable().optional(),
|
|
13
|
+
draft_payload: z.unknown(),
|
|
14
|
+
current_step: z.string().nullable().optional(),
|
|
15
|
+
current_quote_id: z.string().nullable().optional(),
|
|
16
|
+
hold_expires_at: z.string().nullable().optional(),
|
|
17
|
+
consumed_booking_id: z.string().nullable().optional(),
|
|
18
|
+
consumed_at: z.string().nullable().optional(),
|
|
19
|
+
expires_at: z.string(),
|
|
20
|
+
created_at: z.string(),
|
|
21
|
+
updated_at: z.string(),
|
|
22
|
+
});
|
|
23
|
+
/**
|
|
24
|
+
* Server-synced booking draft state. Calls GET on mount (when
|
|
25
|
+
* `enableLoad`) and exposes a `save(draft)` mutation that PUTs
|
|
26
|
+
* to `/catalog/drafts/:id`.
|
|
27
|
+
*
|
|
28
|
+
* The mutation is fire-and-forget from the wizard's perspective —
|
|
29
|
+
* the journey root holds the local draft as the source of truth and
|
|
30
|
+
* the server copy is a recovery surface. Per
|
|
31
|
+
* booking-journey-architecture §5.7.
|
|
32
|
+
*/
|
|
33
|
+
export function useBookingDraft(options) {
|
|
34
|
+
const api = useBookingJourneyApi(options);
|
|
35
|
+
const queryClient = useQueryClient();
|
|
36
|
+
const queryKey = ["booking-draft", options.surface ?? "admin", options.draftId];
|
|
37
|
+
const query = useQuery({
|
|
38
|
+
queryKey,
|
|
39
|
+
queryFn: async () => {
|
|
40
|
+
try {
|
|
41
|
+
return await api.request("GET", `/drafts/${options.draftId}`, draftRowSchema);
|
|
42
|
+
}
|
|
43
|
+
catch (err) {
|
|
44
|
+
if (err instanceof Error && err.message.includes("404"))
|
|
45
|
+
return null;
|
|
46
|
+
throw err;
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
enabled: options.enableLoad !== false,
|
|
50
|
+
});
|
|
51
|
+
const save = useMutation({
|
|
52
|
+
mutationFn: async ({ draft, currentStep, currentQuoteId }) => {
|
|
53
|
+
bookingDraftV1.parse(draft);
|
|
54
|
+
const body = {
|
|
55
|
+
entityModule: draft.entity.module,
|
|
56
|
+
entityId: draft.entity.id,
|
|
57
|
+
draftPayload: draft,
|
|
58
|
+
currentStep,
|
|
59
|
+
currentQuoteId,
|
|
60
|
+
};
|
|
61
|
+
// Only send the source pointer fields when present. They're optional but
|
|
62
|
+
// non-empty when set (`z.string().min(1).optional()`); an empty string —
|
|
63
|
+
// which is what a server-resolved-provenance draft carries — would fail
|
|
64
|
+
// validation. Mirrors the quote hook's guarding.
|
|
65
|
+
if (draft.entity.sourceKind)
|
|
66
|
+
body.sourceKind = draft.entity.sourceKind;
|
|
67
|
+
if (draft.entity.sourceConnectionId)
|
|
68
|
+
body.sourceConnectionId = draft.entity.sourceConnectionId;
|
|
69
|
+
if (draft.entity.sourceRef)
|
|
70
|
+
body.sourceRef = draft.entity.sourceRef;
|
|
71
|
+
return api.request("PUT", `/drafts/${options.draftId}`, draftRowSchema, body);
|
|
72
|
+
},
|
|
73
|
+
onSuccess(row) {
|
|
74
|
+
queryClient.setQueryData(queryKey, row);
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
const remove = useMutation({
|
|
78
|
+
mutationFn: async () => {
|
|
79
|
+
await api.request("DELETE", `/drafts/${options.draftId}`, z.undefined());
|
|
80
|
+
},
|
|
81
|
+
onSuccess() {
|
|
82
|
+
queryClient.setQueryData(queryKey, null);
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
return {
|
|
86
|
+
/** Server-side draft row, or null when missing / 404. */
|
|
87
|
+
serverDraft: query.data ?? null,
|
|
88
|
+
isLoading: query.isLoading,
|
|
89
|
+
save,
|
|
90
|
+
remove,
|
|
91
|
+
refetch: query.refetch,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type BookingJourneyApiOptions } from "./use-booking-journey-api.js";
|
|
2
|
+
export interface PlaceHoldInput {
|
|
3
|
+
entityModule: string;
|
|
4
|
+
entityId: string;
|
|
5
|
+
draftId: string;
|
|
6
|
+
ttlMs?: number;
|
|
7
|
+
parameters?: Record<string, unknown>;
|
|
8
|
+
}
|
|
9
|
+
export interface ReleaseHoldInput {
|
|
10
|
+
entityModule: string;
|
|
11
|
+
holdToken: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Place an inventory soft-hold against the engine. Per
|
|
15
|
+
* booking-journey-architecture §5.7 — the journey calls this once
|
|
16
|
+
* the user has picked a slot + pax in the Configure step so
|
|
17
|
+
* concurrent shoppers can't oversell capacity.
|
|
18
|
+
*
|
|
19
|
+
* The bound mutation also exposes a `release` helper for the cancel
|
|
20
|
+
* path; the reaper covers expiry without explicit calls.
|
|
21
|
+
*/
|
|
22
|
+
export declare function useBookingHold(options?: BookingJourneyApiOptions): {
|
|
23
|
+
place: (input: PlaceHoldInput) => Promise<{
|
|
24
|
+
holdToken: string;
|
|
25
|
+
expiresAt: string;
|
|
26
|
+
}>;
|
|
27
|
+
release: (input: ReleaseHoldInput) => Promise<void>;
|
|
28
|
+
isPending: boolean;
|
|
29
|
+
};
|
|
30
|
+
//# sourceMappingURL=use-booking-hold.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-booking-hold.d.ts","sourceRoot":"","sources":["../../src/booking-engine/use-booking-hold.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,KAAK,wBAAwB,EAAwB,MAAM,8BAA8B,CAAA;AAOlG,MAAM,WAAW,cAAc;IAC7B,YAAY,EAAE,MAAM,CAAA;IACpB,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACrC;AAED,MAAM,WAAW,gBAAgB;IAC/B,YAAY,EAAE,MAAM,CAAA;IACpB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,OAAO,GAAE,wBAA6B,GAAG;IACtE,KAAK,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IACnF,OAAO,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACnD,SAAS,EAAE,OAAO,CAAA;CACnB,CA6BA"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useMutation } from "@tanstack/react-query";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { useBookingJourneyApi } from "./use-booking-journey-api.js";
|
|
5
|
+
const placeHoldResponseSchema = z.object({
|
|
6
|
+
holdToken: z.string(),
|
|
7
|
+
expiresAt: z.string().datetime(),
|
|
8
|
+
});
|
|
9
|
+
/**
|
|
10
|
+
* Place an inventory soft-hold against the engine. Per
|
|
11
|
+
* booking-journey-architecture §5.7 — the journey calls this once
|
|
12
|
+
* the user has picked a slot + pax in the Configure step so
|
|
13
|
+
* concurrent shoppers can't oversell capacity.
|
|
14
|
+
*
|
|
15
|
+
* The bound mutation also exposes a `release` helper for the cancel
|
|
16
|
+
* path; the reaper covers expiry without explicit calls.
|
|
17
|
+
*/
|
|
18
|
+
export function useBookingHold(options = {}) {
|
|
19
|
+
const api = useBookingJourneyApi(options);
|
|
20
|
+
const place = useMutation({
|
|
21
|
+
mutationFn: async (input) => {
|
|
22
|
+
return api.request("POST", "/holds/place", placeHoldResponseSchema, {
|
|
23
|
+
entityModule: input.entityModule,
|
|
24
|
+
entityId: input.entityId,
|
|
25
|
+
draftId: input.draftId,
|
|
26
|
+
ttlMs: input.ttlMs,
|
|
27
|
+
parameters: input.parameters,
|
|
28
|
+
});
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
const release = useMutation({
|
|
32
|
+
mutationFn: async (input) => {
|
|
33
|
+
await api.request("POST", "/holds/release", z.undefined(), {
|
|
34
|
+
entityModule: input.entityModule,
|
|
35
|
+
holdToken: input.holdToken,
|
|
36
|
+
});
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
return {
|
|
40
|
+
place: (input) => place.mutateAsync(input),
|
|
41
|
+
release: (input) => release.mutateAsync(input),
|
|
42
|
+
isPending: place.isPending || release.isPending,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { z } from "zod";
|
|
2
|
+
import { type VoyantFetcher } from "../client.js";
|
|
3
|
+
export interface BookingJourneyApiOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Surface to call against. Operator passes `"admin"`; storefront /
|
|
6
|
+
* partner / embedded surfaces pass `"public"`. Switches the base
|
|
7
|
+
* path between `/v1/admin/catalog` and `/v1/public/catalog`.
|
|
8
|
+
*/
|
|
9
|
+
surface?: "admin" | "public";
|
|
10
|
+
/** Override the API base URL pulled from VoyantCatalogProvider. */
|
|
11
|
+
baseUrl?: string;
|
|
12
|
+
/** Override the fetcher pulled from VoyantCatalogProvider. */
|
|
13
|
+
fetcher?: VoyantFetcher;
|
|
14
|
+
}
|
|
15
|
+
export interface UseBookingJourneyApi {
|
|
16
|
+
/** Resolved base URL for catalog booking-engine endpoints. */
|
|
17
|
+
apiBase: string;
|
|
18
|
+
fetcher: VoyantFetcher;
|
|
19
|
+
/** GET / POST / PUT / DELETE wrappers with Zod-validated responses. */
|
|
20
|
+
request<TOut>(method: "GET" | "POST" | "PUT" | "DELETE", path: string, schema: z.ZodType<TOut>, body?: unknown): Promise<TOut>;
|
|
21
|
+
}
|
|
22
|
+
export declare function useBookingJourneyApi(options?: BookingJourneyApiOptions): UseBookingJourneyApi;
|
|
23
|
+
//# sourceMappingURL=use-booking-journey-api.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-booking-journey-api.d.ts","sourceRoot":"","sources":["../../src/booking-engine/use-booking-journey-api.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAC5B,OAAO,EAGL,KAAK,aAAa,EACnB,MAAM,cAAc,CAAA;AAGrB,MAAM,WAAW,wBAAwB;IACvC;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAA;IAC5B,mEAAmE;IACnE,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,8DAA8D;IAC9D,OAAO,CAAC,EAAE,aAAa,CAAA;CACxB;AAED,MAAM,WAAW,oBAAoB;IACnC,8DAA8D;IAC9D,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,aAAa,CAAA;IACtB,uEAAuE;IACvE,OAAO,CAAC,IAAI,EACV,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,EACzC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,EACvB,IAAI,CAAC,EAAE,OAAO,GACb,OAAO,CAAC,IAAI,CAAC,CAAA;CACjB;AAED,wBAAgB,oBAAoB,CAAC,OAAO,GAAE,wBAA6B,GAAG,oBAAoB,CAuBjG"}
|