@voyantjs/bookings-ui 0.107.0 → 0.108.0
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/dist/components/option-units-stepper-section.d.ts +9 -1
- package/dist/components/option-units-stepper-section.d.ts.map +1 -1
- package/dist/components/option-units-stepper-section.js +10 -2
- package/dist/components/person-picker-section.d.ts +7 -1
- package/dist/components/person-picker-section.d.ts.map +1 -1
- package/dist/components/person-picker-section.js +2 -2
- package/dist/i18n/en.d.ts +37 -1
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +40 -4
- package/dist/i18n/messages.d.ts +37 -1
- package/dist/i18n/messages.d.ts.map +1 -1
- package/dist/i18n/provider.d.ts +74 -2
- package/dist/i18n/provider.d.ts.map +1 -1
- package/dist/i18n/ro.d.ts +37 -1
- package/dist/i18n/ro.d.ts.map +1 -1
- package/dist/i18n/ro.js +39 -3
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/journey/components/booking-journey.d.ts.map +1 -1
- package/dist/journey/components/booking-journey.js +270 -27
- package/dist/journey/components/journey-steps/accommodation-step.d.ts +3 -0
- package/dist/journey/components/journey-steps/accommodation-step.d.ts.map +1 -0
- package/dist/journey/components/journey-steps/accommodation-step.js +71 -0
- package/dist/journey/components/journey-steps/addons-step.d.ts +3 -0
- package/dist/journey/components/journey-steps/addons-step.d.ts.map +1 -0
- package/dist/journey/components/journey-steps/addons-step.js +40 -0
- package/dist/journey/components/journey-steps/billing-step.d.ts +8 -0
- package/dist/journey/components/journey-steps/billing-step.d.ts.map +1 -0
- package/dist/journey/components/journey-steps/billing-step.js +78 -0
- package/dist/journey/components/journey-steps/configure-steps.d.ts +28 -0
- package/dist/journey/components/journey-steps/configure-steps.d.ts.map +1 -0
- package/dist/journey/components/journey-steps/configure-steps.js +231 -0
- package/dist/journey/components/journey-steps/documents-step.d.ts +11 -0
- package/dist/journey/components/journey-steps/documents-step.d.ts.map +1 -0
- package/dist/journey/components/journey-steps/documents-step.js +36 -0
- package/dist/journey/components/journey-steps/payment-step.d.ts +29 -0
- package/dist/journey/components/journey-steps/payment-step.d.ts.map +1 -0
- package/dist/journey/components/journey-steps/payment-step.js +224 -0
- package/dist/journey/components/journey-steps/review-step.d.ts +27 -0
- package/dist/journey/components/journey-steps/review-step.d.ts.map +1 -0
- package/dist/journey/components/journey-steps/review-step.js +18 -0
- package/dist/journey/components/journey-steps/shared.d.ts +75 -0
- package/dist/journey/components/journey-steps/shared.d.ts.map +1 -0
- package/dist/journey/components/journey-steps/shared.js +108 -0
- package/dist/journey/components/journey-steps/travelers-step.d.ts +7 -0
- package/dist/journey/components/journey-steps/travelers-step.d.ts.map +1 -0
- package/dist/journey/components/journey-steps/travelers-step.js +201 -0
- package/dist/journey/components/journey-steps.d.ts +13 -39
- package/dist/journey/components/journey-steps.d.ts.map +1 -1
- package/dist/journey/components/journey-steps.js +16 -613
- package/dist/journey/components/side-panel.d.ts +7 -2
- package/dist/journey/components/side-panel.d.ts.map +1 -1
- package/dist/journey/components/side-panel.js +73 -24
- package/dist/journey/index.d.ts +2 -2
- package/dist/journey/index.d.ts.map +1 -1
- package/dist/journey/index.js +1 -1
- package/dist/journey/lib/pax-band-dependencies.d.ts +27 -0
- package/dist/journey/lib/pax-band-dependencies.d.ts.map +1 -0
- package/dist/journey/lib/pax-band-dependencies.js +50 -0
- package/dist/journey/lib/payment-schedule.d.ts +19 -0
- package/dist/journey/lib/payment-schedule.d.ts.map +1 -0
- package/dist/journey/lib/payment-schedule.js +90 -0
- package/dist/journey/types.d.ts +141 -8
- package/dist/journey/types.d.ts.map +1 -1
- package/dist/journey/types.js +3 -1
- package/package.json +32 -32
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { Separator } from "@voyantjs/ui/components";
|
|
4
|
+
import { Card, CardContent, CardHeader, CardTitle } from "@voyantjs/ui/components/card";
|
|
5
|
+
import { CountryCombobox } from "@voyantjs/ui/components/country-combobox";
|
|
6
|
+
import { Label } from "@voyantjs/ui/components/label";
|
|
7
|
+
import { RadioGroup, RadioGroupItem } from "@voyantjs/ui/components/radio-group";
|
|
8
|
+
import { useBookingsUiMessagesOrDefault } from "../../../i18n/index.js";
|
|
9
|
+
import { patchBilling } from "../../lib/draft-state.js";
|
|
10
|
+
import { Field, JourneyWarnings, PhoneField } from "./shared.js";
|
|
11
|
+
// ─────────────────────────────────────────────────────────────────
|
|
12
|
+
// Billing
|
|
13
|
+
// ─────────────────────────────────────────────────────────────────
|
|
14
|
+
export function BillingStep({ draft, setDraft, renderLeadContactPicker, renderExtras, warnings, }) {
|
|
15
|
+
const messages = useBookingsUiMessagesOrDefault();
|
|
16
|
+
const billing = draft.billing;
|
|
17
|
+
// Merge each partial from the picker (person record, org record, address
|
|
18
|
+
// lookup) into the billing draft without clobbering the other slices.
|
|
19
|
+
const apply = (next) => {
|
|
20
|
+
const patch = {};
|
|
21
|
+
if (next.firstName !== undefined ||
|
|
22
|
+
next.lastName !== undefined ||
|
|
23
|
+
next.email !== undefined ||
|
|
24
|
+
next.phone !== undefined ||
|
|
25
|
+
next.personId !== undefined) {
|
|
26
|
+
patch.contact = {
|
|
27
|
+
...billing.contact,
|
|
28
|
+
...(next.firstName !== undefined ? { firstName: next.firstName } : {}),
|
|
29
|
+
...(next.lastName !== undefined ? { lastName: next.lastName } : {}),
|
|
30
|
+
...(next.email !== undefined ? { email: next.email } : {}),
|
|
31
|
+
...(next.phone !== undefined ? { phone: next.phone } : {}),
|
|
32
|
+
...(next.personId !== undefined ? { personId: next.personId } : {}),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
if (next.organizationId !== undefined) {
|
|
36
|
+
patch.organizationId = next.organizationId;
|
|
37
|
+
}
|
|
38
|
+
if (next.companyName !== undefined || next.taxId !== undefined) {
|
|
39
|
+
patch.company = {
|
|
40
|
+
name: next.companyName ?? billing.company?.name ?? "",
|
|
41
|
+
vatId: next.taxId ?? billing.company?.vatId,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
if (next.address) {
|
|
45
|
+
patch.address = { ...billing.address, ...next.address };
|
|
46
|
+
}
|
|
47
|
+
setDraft(patchBilling(draft, patch));
|
|
48
|
+
};
|
|
49
|
+
return (_jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { children: messages.bookingJourney.billing.title }) }), _jsx(Separator, {}), _jsxs(CardContent, { className: "space-y-6", children: [_jsxs("div", { className: "space-y-2", children: [_jsx(Label, { children: messages.bookingJourney.billing.buyerType }), _jsxs(RadioGroup, { value: billing.buyerType, onValueChange: (v) => setDraft(patchBilling(draft, { buyerType: v })), className: "flex gap-4", children: [_jsxs("label", { className: "flex items-center gap-2 text-sm", children: [_jsx(RadioGroupItem, { value: "B2C" }), " ", messages.bookingJourney.billing.individual] }), _jsxs("label", { className: "flex items-center gap-2 text-sm", children: [_jsx(RadioGroupItem, { value: "B2B" }), " ", messages.bookingJourney.billing.company] })] })] }), renderLeadContactPicker ? (_jsx("div", { children: renderLeadContactPicker({ apply, buyerType: billing.buyerType }) })) : null, renderLeadContactPicker ? null : (_jsxs(_Fragment, { children: [_jsxs("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-2", children: [_jsx(Field, { id: "bj-billing-firstName", label: messages.bookingJourney.billing.firstName, value: billing.contact.firstName, onChange: (v) => setDraft(patchBilling(draft, {
|
|
50
|
+
contact: { ...billing.contact, firstName: v },
|
|
51
|
+
})) }), _jsx(Field, { id: "bj-billing-lastName", label: messages.bookingJourney.billing.lastName, value: billing.contact.lastName, onChange: (v) => setDraft(patchBilling(draft, {
|
|
52
|
+
contact: { ...billing.contact, lastName: v },
|
|
53
|
+
})) }), _jsx(Field, { id: "bj-billing-email", label: messages.bookingJourney.billing.email, type: "email", value: billing.contact.email, onChange: (v) => setDraft(patchBilling(draft, {
|
|
54
|
+
contact: { ...billing.contact, email: v },
|
|
55
|
+
})) }), _jsx(PhoneField, { id: "bj-billing-phone", label: messages.bookingJourney.billing.phone, value: billing.contact.phone ?? "", onChange: (v) => setDraft(patchBilling(draft, {
|
|
56
|
+
contact: { ...billing.contact, phone: v },
|
|
57
|
+
})) })] }), _jsxs("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-2", children: [_jsx(Field, { id: "bj-billing-line1", label: messages.bookingJourney.billing.addressLine1, value: billing.address.line1 ?? "", onChange: (v) => setDraft(patchBilling(draft, {
|
|
58
|
+
address: { ...billing.address, line1: v },
|
|
59
|
+
})) }), _jsx(Field, { id: "bj-billing-line2", label: messages.bookingJourney.billing.addressLine2Optional, value: billing.address.line2 ?? "", onChange: (v) => setDraft(patchBilling(draft, {
|
|
60
|
+
address: { ...billing.address, line2: v },
|
|
61
|
+
})) }), _jsx(Field, { id: "bj-billing-city", label: messages.bookingJourney.billing.city, value: billing.address.city ?? "", onChange: (v) => setDraft(patchBilling(draft, {
|
|
62
|
+
address: { ...billing.address, city: v },
|
|
63
|
+
})) }), _jsx(Field, { id: "bj-billing-postal", label: messages.bookingJourney.billing.postalCode, value: billing.address.postal ?? "", onChange: (v) => setDraft(patchBilling(draft, {
|
|
64
|
+
address: { ...billing.address, postal: v },
|
|
65
|
+
})) }), _jsxs("div", { className: "space-y-1 sm:col-span-2", children: [_jsx(Label, { htmlFor: "bj-billing-country", children: messages.bookingJourney.billing.country }), _jsx(CountryCombobox, { value: billing.address.country ?? null, onChange: (code) => setDraft(patchBilling(draft, {
|
|
66
|
+
address: { ...billing.address, country: code ?? "" },
|
|
67
|
+
})) })] })] }), billing.buyerType === "B2B" ? (_jsxs("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-2", children: [_jsx(Field, { id: "bj-billing-companyName", label: messages.bookingJourney.billing.companyName, value: billing.company?.name ?? "", onChange: (v) => setDraft(patchBilling(draft, {
|
|
68
|
+
company: {
|
|
69
|
+
...(billing.company ?? { name: "" }),
|
|
70
|
+
name: v,
|
|
71
|
+
},
|
|
72
|
+
})) }), _jsx(Field, { id: "bj-billing-vatId", label: messages.bookingJourney.billing.vatId, value: billing.company?.vatId ?? "", onChange: (v) => setDraft(patchBilling(draft, {
|
|
73
|
+
company: {
|
|
74
|
+
...(billing.company ?? { name: "" }),
|
|
75
|
+
vatId: v,
|
|
76
|
+
},
|
|
77
|
+
})) })] })) : null] })), renderExtras ? _jsx("div", { children: renderExtras() }) : null, _jsx(JourneyWarnings, { warnings: warnings })] })] }));
|
|
78
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { BookingDraftShape } from "@voyantjs/catalog/booking-engine";
|
|
2
|
+
import { type Draft } from "../../lib/draft-state.js";
|
|
3
|
+
import { type RenderDeparturePicker, type RenderUnitsPicker, type StepCommonProps } from "./shared.js";
|
|
4
|
+
export declare function DepartureStep({ draft, setDraft, shape, productId, renderDeparturePicker, }: StepCommonProps & {
|
|
5
|
+
/** Owned product id — passed to the injected departure picker. */
|
|
6
|
+
productId?: string;
|
|
7
|
+
renderDeparturePicker?: RenderDeparturePicker;
|
|
8
|
+
}): React.ReactElement;
|
|
9
|
+
export declare function OptionsStep({ draft, setDraft, shape, productId, renderUnitsPicker, }: StepCommonProps & {
|
|
10
|
+
/** Owned product id — passed to the injected units picker. */
|
|
11
|
+
productId?: string;
|
|
12
|
+
renderUnitsPicker?: RenderUnitsPicker;
|
|
13
|
+
}): React.ReactElement;
|
|
14
|
+
export declare function PaxBands({ draft, setDraft, shape }: StepCommonProps): React.ReactElement;
|
|
15
|
+
export declare function PaxValidation({ draft, shape, }: {
|
|
16
|
+
draft: Draft;
|
|
17
|
+
shape: BookingDraftShape;
|
|
18
|
+
}): React.ReactNode;
|
|
19
|
+
/**
|
|
20
|
+
* Surfaces broken cross-band occupancy rules (e.g. "Child under 6
|
|
21
|
+
* requires an Adult") as hard validation errors under the pax steppers.
|
|
22
|
+
* These also block step advancement via `canAdvanceFromStep`.
|
|
23
|
+
*/
|
|
24
|
+
export declare function PaxDependencyWarnings({ draft, shape, }: {
|
|
25
|
+
draft: Draft;
|
|
26
|
+
shape: BookingDraftShape;
|
|
27
|
+
}): React.ReactNode;
|
|
28
|
+
//# sourceMappingURL=configure-steps.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"configure-steps.d.ts","sourceRoot":"","sources":["../../../../src/journey/components/journey-steps/configure-steps.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAA;AASzE,OAAO,EAAE,KAAK,KAAK,EAA2C,MAAM,0BAA0B,CAAA;AAK9F,OAAO,EAGL,KAAK,qBAAqB,EAC1B,KAAK,iBAAiB,EACtB,KAAK,eAAe,EACrB,MAAM,aAAa,CAAA;AAMpB,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,QAAQ,EACR,KAAK,EACL,SAAS,EACT,qBAAqB,GACtB,EAAE,eAAe,GAAG;IACnB,kEAAkE;IAClE,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,qBAAqB,CAAC,EAAE,qBAAqB,CAAA;CAC9C,GAAG,KAAK,CAAC,YAAY,CA0CrB;AAED,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,QAAQ,EACR,KAAK,EACL,SAAS,EACT,iBAAiB,GAClB,EAAE,eAAe,GAAG;IACnB,8DAA8D;IAC9D,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,iBAAiB,CAAC,EAAE,iBAAiB,CAAA;CACtC,GAAG,KAAK,CAAC,YAAY,CAiErB;AAED,wBAAgB,QAAQ,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,eAAe,GAAG,KAAK,CAAC,YAAY,CAsDxF;AAED,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,KAAK,GACN,EAAE;IACD,KAAK,EAAE,KAAK,CAAA;IACZ,KAAK,EAAE,iBAAiB,CAAA;CACzB,GAAG,KAAK,CAAC,SAAS,CAwBlB;AAgCD;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,EACpC,KAAK,EACL,KAAK,GACN,EAAE;IACD,KAAK,EAAE,KAAK,CAAA;IACZ,KAAK,EAAE,iBAAiB,CAAA;CACzB,GAAG,KAAK,CAAC,SAAS,CAoBlB"}
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Separator } from "@voyantjs/ui/components";
|
|
4
|
+
import { Button } from "@voyantjs/ui/components/button";
|
|
5
|
+
import { Card, CardContent, CardHeader, CardTitle } from "@voyantjs/ui/components/card";
|
|
6
|
+
import { Input } from "@voyantjs/ui/components/input";
|
|
7
|
+
import { Label } from "@voyantjs/ui/components/label";
|
|
8
|
+
import { RadioGroup, RadioGroupItem } from "@voyantjs/ui/components/radio-group";
|
|
9
|
+
import { Minus, Plus } from "lucide-react";
|
|
10
|
+
import { formatMessage, useBookingsUiMessagesOrDefault } from "../../../i18n/index.js";
|
|
11
|
+
import { patchConfigure, patchPaxCount, totalPax } from "../../lib/draft-state.js";
|
|
12
|
+
import { evaluatePaxBandDependencies, } from "../../lib/pax-band-dependencies.js";
|
|
13
|
+
import { ageHint, DateField, } from "./shared.js";
|
|
14
|
+
// ─────────────────────────────────────────────────────────────────
|
|
15
|
+
// Configure
|
|
16
|
+
// ─────────────────────────────────────────────────────────────────
|
|
17
|
+
export function DepartureStep({ draft, setDraft, shape, productId, renderDeparturePicker, }) {
|
|
18
|
+
const messages = useBookingsUiMessagesOrDefault();
|
|
19
|
+
const subSteps = shape.configureSubSteps ?? [];
|
|
20
|
+
// With no descriptor sub-steps, still offer a departure (storefront
|
|
21
|
+
// free-date fallback).
|
|
22
|
+
const showsDeparture = subSteps.length === 0 || subSteps.some((s) => s.kind === "departure");
|
|
23
|
+
const departureNode = showsDeparture ? (renderDeparturePicker && productId ? (renderDeparturePicker({
|
|
24
|
+
productId,
|
|
25
|
+
optionId: draft.configure.variantId ?? null,
|
|
26
|
+
slotId: draft.configure.departureSlotId ?? null,
|
|
27
|
+
departureDate: draft.configure.departureDate ?? null,
|
|
28
|
+
departureTime: draft.configure.departureTime ?? null,
|
|
29
|
+
onChange: (next) => setDraft(patchConfigure(draft, {
|
|
30
|
+
...(next.slotId !== undefined ? { departureSlotId: next.slotId ?? undefined } : {}),
|
|
31
|
+
...(next.departureDate !== undefined
|
|
32
|
+
? { departureDate: next.departureDate ?? undefined }
|
|
33
|
+
: {}),
|
|
34
|
+
...(next.departureTime !== undefined
|
|
35
|
+
? { departureTime: next.departureTime ?? undefined }
|
|
36
|
+
: {}),
|
|
37
|
+
})),
|
|
38
|
+
})) : (_jsx(DepartureBasic, { draft: draft, setDraft: setDraft }))) : null;
|
|
39
|
+
return (_jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { children: messages.bookingJourney.steps.departure }) }), _jsx(Separator, {}), _jsx(CardContent, { className: "space-y-6", children: departureNode })] }));
|
|
40
|
+
}
|
|
41
|
+
export function OptionsStep({ draft, setDraft, shape, productId, renderUnitsPicker, }) {
|
|
42
|
+
const messages = useBookingsUiMessagesOrDefault();
|
|
43
|
+
const subSteps = shape.configureSubSteps ?? [];
|
|
44
|
+
const optionList = subSteps.flatMap((s) => (s.kind === "product-option" ? s.options : []));
|
|
45
|
+
const multipleOptions = optionList.length > 1;
|
|
46
|
+
const showsUnits = subSteps.some((s) => s.kind === "option-units");
|
|
47
|
+
const otherSteps = subSteps.filter((s) => s.kind !== "departure" &&
|
|
48
|
+
s.kind !== "product-option" &&
|
|
49
|
+
s.kind !== "option-units" &&
|
|
50
|
+
s.kind !== "occupancy");
|
|
51
|
+
const unitsNode = showsUnits && renderUnitsPicker && productId
|
|
52
|
+
? renderUnitsPicker({
|
|
53
|
+
productId,
|
|
54
|
+
optionId: draft.configure.variantId ?? null,
|
|
55
|
+
slotId: draft.configure.departureSlotId ?? null,
|
|
56
|
+
selections: draft.configure.optionSelections ?? [],
|
|
57
|
+
onChange: (selections) => setDraft(patchConfigure(draft, { optionSelections: selections })),
|
|
58
|
+
})
|
|
59
|
+
: null;
|
|
60
|
+
const optionNode = optionList.length > 0 ? (_jsx(ProductOptionFields, { draft: draft, setDraft: setDraft, options: optionList,
|
|
61
|
+
// With a real choice between options, nest the rooms under the
|
|
62
|
+
// SELECTED option so switching reveals that option's inventory in
|
|
63
|
+
// place. With a single/no option there's nothing to switch, so the
|
|
64
|
+
// rooms render directly below instead.
|
|
65
|
+
renderSelectedUnits: multipleOptions && unitsNode ? () => unitsNode : undefined })) : null;
|
|
66
|
+
return (_jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { children: messages.bookingJourney.steps.options }) }), _jsx(Separator, {}), _jsxs(CardContent, { className: "space-y-6", children: [optionNode || unitsNode ? (_jsxs("div", { className: "space-y-3", children: [optionNode, unitsNode && !multipleOptions ? unitsNode : null] })) : null, otherSteps.length > 0 ? (_jsx("div", { className: "space-y-4", children: otherSteps.map((sub) => renderOtherConfigureSubStep(sub, draft, setDraft)) })) : null] })] }));
|
|
67
|
+
}
|
|
68
|
+
export function PaxBands({ draft, setDraft, shape }) {
|
|
69
|
+
const messages = useBookingsUiMessagesOrDefault();
|
|
70
|
+
return (_jsxs("div", { className: "flex flex-col gap-2 rounded-md border p-3", children: [_jsx(Label, { children: messages.bookingJourney.travelers.partySize }), _jsx("div", { className: "flex flex-col gap-2", children: shape.paxBands.map((band) => {
|
|
71
|
+
const value = draft.configure.pax?.[band.code] ?? 0;
|
|
72
|
+
return (_jsxs("div", { className: "flex items-center gap-3 rounded-md border px-3 py-2", children: [_jsxs("div", { className: "flex-1", children: [_jsx("div", { className: "text-sm font-medium", children: band.label }), band.minAge != null || band.maxAge != null ? (_jsx("div", { className: "text-muted-foreground text-xs", children: ageHint(band.minAge, band.maxAge, messages) })) : null] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Button, { variant: "ghost", size: "sm", type: "button", className: "h-7 w-7 p-0", disabled: value <= band.minCount, onClick: () => setDraft(patchPaxCount(draft, band.code, value - 1)), "aria-label": formatMessage(messages.bookingJourney.travelers.decrease, {
|
|
73
|
+
label: band.label,
|
|
74
|
+
}), children: _jsx(Minus, { className: "h-3.5 w-3.5" }) }), _jsx("span", { className: "min-w-[1.5rem] text-center text-sm tabular-nums", children: value }), _jsx(Button, { variant: "ghost", size: "sm", type: "button", className: "h-7 w-7 p-0", disabled: value >= band.maxCount, onClick: () => setDraft(patchPaxCount(draft, band.code, value + 1)), "aria-label": formatMessage(messages.bookingJourney.travelers.increase, {
|
|
75
|
+
label: band.label,
|
|
76
|
+
}), children: _jsx(Plus, { className: "h-3.5 w-3.5" }) })] })] }, band.code));
|
|
77
|
+
}) }), _jsx(PaxValidation, { draft: draft, shape: shape })] }));
|
|
78
|
+
}
|
|
79
|
+
export function PaxValidation({ draft, shape, }) {
|
|
80
|
+
const messages = useBookingsUiMessagesOrDefault();
|
|
81
|
+
const total = totalPax(draft);
|
|
82
|
+
const { min, max } = shape.paxBandsAllowedTotal;
|
|
83
|
+
if (total < min) {
|
|
84
|
+
return (_jsx("p", { className: "text-sm text-amber-600", children: formatMessage(messages.bookingJourney.validation.addAtLeastTravelers, {
|
|
85
|
+
count: min,
|
|
86
|
+
plural: min === 1 ? "" : "s",
|
|
87
|
+
}) }));
|
|
88
|
+
}
|
|
89
|
+
if (total > max) {
|
|
90
|
+
return (_jsx("p", { className: "text-sm text-destructive", children: formatMessage(messages.bookingJourney.validation.maxTravelersPerBooking, {
|
|
91
|
+
count: max,
|
|
92
|
+
}) }));
|
|
93
|
+
}
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
/** Formats one occupancy-rule violation into a localized message. */
|
|
97
|
+
function formatPaxDependencyViolation(violation, messages) {
|
|
98
|
+
switch (violation.type) {
|
|
99
|
+
case "requires":
|
|
100
|
+
return formatMessage(messages.dependencyRequires, {
|
|
101
|
+
dependent: violation.dependentLabel,
|
|
102
|
+
master: violation.masterLabel,
|
|
103
|
+
});
|
|
104
|
+
case "excludes":
|
|
105
|
+
return formatMessage(messages.dependencyExcludes, {
|
|
106
|
+
dependent: violation.dependentLabel,
|
|
107
|
+
master: violation.masterLabel,
|
|
108
|
+
});
|
|
109
|
+
case "limits_per_master":
|
|
110
|
+
return formatMessage(messages.dependencyLimitPerMaster, {
|
|
111
|
+
limit: violation.limit ?? 0,
|
|
112
|
+
dependent: violation.dependentLabel,
|
|
113
|
+
master: violation.masterLabel,
|
|
114
|
+
});
|
|
115
|
+
case "limits_sum":
|
|
116
|
+
return formatMessage(messages.dependencyLimitSum, {
|
|
117
|
+
limit: violation.limit ?? 0,
|
|
118
|
+
dependent: violation.dependentLabel,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Surfaces broken cross-band occupancy rules (e.g. "Child under 6
|
|
124
|
+
* requires an Adult") as hard validation errors under the pax steppers.
|
|
125
|
+
* These also block step advancement via `canAdvanceFromStep`.
|
|
126
|
+
*/
|
|
127
|
+
export function PaxDependencyWarnings({ draft, shape, }) {
|
|
128
|
+
const messages = useBookingsUiMessagesOrDefault();
|
|
129
|
+
const violations = evaluatePaxBandDependencies(draft.configure.pax, shape.paxBandDependencies, shape.paxBands);
|
|
130
|
+
if (violations.length === 0)
|
|
131
|
+
return null;
|
|
132
|
+
return (_jsx("div", { className: "space-y-1", children: violations.map((violation) => (_jsx("p", { className: "text-destructive text-sm", children: formatPaxDependencyViolation(violation, messages.bookingJourney.validation) }, `${violation.type}-${violation.dependentCode}-${violation.masterCode}`))) }));
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Renders the vertical-specific Configure sub-steps that aren't part of the
|
|
136
|
+
* fixed products layout (departure / option / rooms / occupancy are handled
|
|
137
|
+
* explicitly in `ConfigureStep`). Cruise cabins, date ranges, and air
|
|
138
|
+
* arrangement land here, in descriptor order.
|
|
139
|
+
*/
|
|
140
|
+
function renderOtherConfigureSubStep(sub, draft, setDraft) {
|
|
141
|
+
if (sub.kind === "date-range") {
|
|
142
|
+
return (_jsx(DateRangeFields, { draft: draft, setDraft: setDraft, minNights: sub.minNights, maxNights: sub.maxNights }, "date-range"));
|
|
143
|
+
}
|
|
144
|
+
if (sub.kind === "cabin-category") {
|
|
145
|
+
return (_jsx(CabinCategoryFields, { draft: draft, setDraft: setDraft, categories: sub.categories }, "cabin-category"));
|
|
146
|
+
}
|
|
147
|
+
if (sub.kind === "cabin-number") {
|
|
148
|
+
return (_jsx(CabinNumberFields, { draft: draft, setDraft: setDraft, perCategory: sub.perCategory }, "cabin-number"));
|
|
149
|
+
}
|
|
150
|
+
if (sub.kind === "air-arrangement") {
|
|
151
|
+
return _jsx(AirArrangementFields, { draft: draft, setDraft: setDraft }, "air-arrangement");
|
|
152
|
+
}
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
function ProductOptionFields({ draft, setDraft, options, renderSelectedUnits, }) {
|
|
156
|
+
const messages = useBookingsUiMessagesOrDefault();
|
|
157
|
+
const selectedId = draft.configure.variantId;
|
|
158
|
+
if (options.length === 0)
|
|
159
|
+
return null;
|
|
160
|
+
return (_jsxs("div", { className: "space-y-2", children: [_jsx(Label, { children: messages.bookingJourney.configure.option }), _jsx(RadioGroup, { value: selectedId ?? "",
|
|
161
|
+
// Switching options must clear the previous option's room/unit picks —
|
|
162
|
+
// otherwise stale `optionSelections` from option A still drive the
|
|
163
|
+
// quote/commit (price, item lines, rooms gate) under option B.
|
|
164
|
+
onValueChange: (v) => setDraft(patchConfigure(draft, v === selectedId ? { variantId: v } : { variantId: v, optionSelections: [] })), className: "grid grid-cols-1 gap-2", children: options.map((option) => {
|
|
165
|
+
const selected = option.id === selectedId;
|
|
166
|
+
return (_jsxs("div", { className: "space-y-2", children: [_jsxs("label", { className: "flex cursor-pointer items-start gap-3 rounded-md border p-3 text-sm transition-colors " +
|
|
167
|
+
(selected ? "border-primary bg-primary/5" : "border-input hover:bg-muted/50"), children: [_jsx(RadioGroupItem, { value: option.id, className: "mt-0.5" }), _jsxs("div", { className: "min-w-0", children: [_jsxs("div", { className: "font-medium", children: [option.name, option.code ? (_jsx("span", { className: "ml-2 text-muted-foreground text-xs uppercase", children: option.code })) : null] }), option.description ? (_jsx("div", { className: "mt-1 text-muted-foreground text-xs", children: option.description })) : null] })] }), selected && renderSelectedUnits ? (_jsx("div", { className: "ml-7 space-y-2 border-muted border-l-2 pl-4", children: renderSelectedUnits() })) : null] }, option.id));
|
|
168
|
+
}) })] }));
|
|
169
|
+
}
|
|
170
|
+
function DepartureBasic({ draft, setDraft, }) {
|
|
171
|
+
const messages = useBookingsUiMessagesOrDefault();
|
|
172
|
+
return (_jsxs("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-2", children: [_jsx(DateField, { id: "bj-departure-date", label: messages.bookingJourney.configure.departureDate, value: draft.configure.departureDate ?? "", onChange: (v) => setDraft(patchConfigure(draft, { departureDate: v })), range: "future" }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "bj-departure-time", children: messages.bookingJourney.configure.timeOptional }), _jsx(Input, { id: "bj-departure-time", type: "time", value: draft.configure.departureTime ?? "", onChange: (e) => setDraft(patchConfigure(draft, { departureTime: e.target.value })) })] })] }));
|
|
173
|
+
}
|
|
174
|
+
function DateRangeFields({ draft, setDraft, minNights, maxNights, }) {
|
|
175
|
+
const messages = useBookingsUiMessagesOrDefault();
|
|
176
|
+
const range = draft.configure.dateRange;
|
|
177
|
+
return (_jsxs("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-2", children: [_jsx(DateField, { id: "bj-checkin", label: messages.bookingJourney.configure.checkIn, value: range?.checkIn ?? "", onChange: (v) => setDraft(patchConfigure(draft, {
|
|
178
|
+
dateRange: { checkIn: v, checkOut: range?.checkOut ?? "" },
|
|
179
|
+
})), range: "future" }), _jsx(DateField, { id: "bj-checkout", label: formatMessage(messages.bookingJourney.configure.checkOutWithNights, {
|
|
180
|
+
minNights,
|
|
181
|
+
maxNights,
|
|
182
|
+
}), value: range?.checkOut ?? "", onChange: (v) => setDraft(patchConfigure(draft, {
|
|
183
|
+
dateRange: { checkIn: range?.checkIn ?? "", checkOut: v },
|
|
184
|
+
})), range: "future" })] }));
|
|
185
|
+
}
|
|
186
|
+
function CabinCategoryFields({ draft, setDraft, categories, }) {
|
|
187
|
+
const messages = useBookingsUiMessagesOrDefault();
|
|
188
|
+
return (_jsxs("div", { className: "space-y-2", children: [_jsx(Label, { children: messages.bookingJourney.configure.cabinCategory }), _jsx("div", { className: "grid grid-cols-1 gap-2 sm:grid-cols-2", children: categories.map((cat) => {
|
|
189
|
+
const selected = draft.configure.cabinCategoryId === cat.id;
|
|
190
|
+
return (_jsxs("button", { type: "button", className: `rounded-md border p-3 text-left ${selected ? "border-primary ring-2 ring-primary" : ""}`, onClick: () => setDraft(patchConfigure(draft, {
|
|
191
|
+
cabinCategoryId: cat.id,
|
|
192
|
+
cabinNumberId: undefined,
|
|
193
|
+
})), children: [_jsx("div", { className: "font-medium", children: cat.name }), cat.description ? (_jsx("div", { className: "text-muted-foreground text-xs", children: cat.description })) : null] }, cat.id));
|
|
194
|
+
}) })] }));
|
|
195
|
+
}
|
|
196
|
+
function AirArrangementFields({ draft, setDraft, }) {
|
|
197
|
+
const messages = useBookingsUiMessagesOrDefault();
|
|
198
|
+
const current = draft.configure.airArrangement;
|
|
199
|
+
const options = [
|
|
200
|
+
{
|
|
201
|
+
value: "cruise_line",
|
|
202
|
+
label: messages.bookingJourney.configure.airOptions.cruise_line.label,
|
|
203
|
+
description: messages.bookingJourney.configure.airOptions.cruise_line.description,
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
value: "independent",
|
|
207
|
+
label: messages.bookingJourney.configure.airOptions.independent.label,
|
|
208
|
+
description: messages.bookingJourney.configure.airOptions.independent.description,
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
value: "none",
|
|
212
|
+
label: messages.bookingJourney.configure.airOptions.none.label,
|
|
213
|
+
description: messages.bookingJourney.configure.airOptions.none.description,
|
|
214
|
+
},
|
|
215
|
+
];
|
|
216
|
+
return (_jsxs("div", { className: "space-y-2", children: [_jsx(Label, { children: messages.bookingJourney.configure.airArrangements }), _jsx("div", { className: "grid grid-cols-1 gap-2 sm:grid-cols-3", children: options.map((opt) => {
|
|
217
|
+
const selected = current === opt.value;
|
|
218
|
+
return (_jsxs("button", { type: "button", className: `rounded-md border p-3 text-left text-sm ${selected ? "border-primary ring-2 ring-primary" : ""}`, onClick: () => setDraft(patchConfigure(draft, { airArrangement: opt.value })), children: [_jsx("div", { className: "font-medium", children: opt.label }), _jsx("div", { className: "text-muted-foreground text-xs", children: opt.description })] }, opt.value));
|
|
219
|
+
}) })] }));
|
|
220
|
+
}
|
|
221
|
+
function CabinNumberFields({ draft, setDraft, perCategory, }) {
|
|
222
|
+
const messages = useBookingsUiMessagesOrDefault();
|
|
223
|
+
const catId = draft.configure.cabinCategoryId;
|
|
224
|
+
if (!catId)
|
|
225
|
+
return null;
|
|
226
|
+
const cabins = perCategory[catId] ?? [];
|
|
227
|
+
return (_jsxs("div", { className: "space-y-2", children: [_jsx(Label, { children: messages.bookingJourney.configure.cabinNumber }), _jsx("div", { className: "grid grid-cols-2 gap-2 sm:grid-cols-4", children: cabins.map((cabin) => {
|
|
228
|
+
const selected = draft.configure.cabinNumberId === cabin.id;
|
|
229
|
+
return (_jsx("button", { type: "button", className: `rounded-md border p-2 text-sm ${selected ? "border-primary ring-2 ring-primary" : ""}`, onClick: () => setDraft(patchConfigure(draft, { cabinNumberId: cabin.id })), children: cabin.label }, cabin.id));
|
|
230
|
+
}) })] }));
|
|
231
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Draft } from "../../lib/draft-state.js";
|
|
2
|
+
/**
|
|
3
|
+
* Operator-only finalization that isn't about payment: an internal note and the
|
|
4
|
+
* document-generation settings (proforma vs invoice+contract, notify). Split
|
|
5
|
+
* out of the Payment block since these don't affect the amount due.
|
|
6
|
+
*/
|
|
7
|
+
export declare function DocumentsStep({ draft, setDraft, }: {
|
|
8
|
+
draft: Draft;
|
|
9
|
+
setDraft: (next: Draft) => void;
|
|
10
|
+
}): React.ReactElement;
|
|
11
|
+
//# sourceMappingURL=documents-step.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"documents-step.d.ts","sourceRoot":"","sources":["../../../../src/journey/components/journey-steps/documents-step.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAA;AAErD;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,QAAQ,GACT,EAAE;IACD,KAAK,EAAE,KAAK,CAAA;IACZ,QAAQ,EAAE,CAAC,IAAI,EAAE,KAAK,KAAK,IAAI,CAAA;CAChC,GAAG,KAAK,CAAC,YAAY,CA6GrB"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Card, CardContent, CardHeader, CardTitle } from "@voyantjs/ui/components/card";
|
|
4
|
+
import { Checkbox } from "@voyantjs/ui/components/checkbox";
|
|
5
|
+
import { Label } from "@voyantjs/ui/components/label";
|
|
6
|
+
import { Separator } from "@voyantjs/ui/components/separator";
|
|
7
|
+
import { Textarea } from "@voyantjs/ui/components/textarea";
|
|
8
|
+
import { useBookingsUiMessagesOrDefault } from "../../../i18n/index.js";
|
|
9
|
+
/**
|
|
10
|
+
* Operator-only finalization that isn't about payment: an internal note and the
|
|
11
|
+
* document-generation settings (proforma vs invoice+contract, notify). Split
|
|
12
|
+
* out of the Payment block since these don't affect the amount due.
|
|
13
|
+
*/
|
|
14
|
+
export function DocumentsStep({ draft, setDraft, }) {
|
|
15
|
+
const messages = useBookingsUiMessagesOrDefault();
|
|
16
|
+
return (_jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { children: messages.bookingJourney.steps.documents }) }), _jsx(Separator, {}), _jsxs(CardContent, { className: "space-y-4", children: [_jsxs("div", { className: "flex items-start gap-2 text-sm", children: [_jsx(Checkbox, { id: "bj-save-as-draft", checked: draft.saveAsDraft === true, onCheckedChange: (v) => setDraft({ ...draft, saveAsDraft: v === true }), className: "mt-0.5" }), _jsxs("div", { className: "flex flex-col gap-1", children: [_jsx(Label, { htmlFor: "bj-save-as-draft", className: "cursor-pointer", children: messages.bookingJourney.documents.saveAsDraft }), _jsx("p", { className: "text-muted-foreground text-xs", children: messages.bookingJourney.documents.saveAsDraftHint })] })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "bj-internal-notes", children: messages.bookingJourney.review.internalNotes }), _jsx(Textarea, { id: "bj-internal-notes", value: draft.internalNotes ?? "", onChange: (e) => setDraft({ ...draft, internalNotes: e.target.value }) })] }), _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: "bj-doc-proforma", checked: draft.documentGeneration?.invoiceType === "proforma", onCheckedChange: (v) => setDraft({
|
|
17
|
+
...draft,
|
|
18
|
+
documentGeneration: v === true
|
|
19
|
+
? {
|
|
20
|
+
contractDocument: false,
|
|
21
|
+
invoiceDocument: true,
|
|
22
|
+
invoiceType: "proforma",
|
|
23
|
+
}
|
|
24
|
+
: undefined,
|
|
25
|
+
}) }), _jsx(Label, { htmlFor: "bj-doc-proforma", className: "cursor-pointer", children: messages.bookingCreateDialog.labels.generateProforma })] }), _jsxs("div", { className: "flex items-center gap-2 text-sm", children: [_jsx(Checkbox, { id: "bj-doc-invoice-contract", checked: draft.documentGeneration?.contractDocument === true &&
|
|
26
|
+
draft.documentGeneration?.invoiceType !== "proforma", onCheckedChange: (v) => setDraft({
|
|
27
|
+
...draft,
|
|
28
|
+
documentGeneration: v === true
|
|
29
|
+
? {
|
|
30
|
+
contractDocument: true,
|
|
31
|
+
invoiceDocument: true,
|
|
32
|
+
invoiceType: "invoice",
|
|
33
|
+
}
|
|
34
|
+
: undefined,
|
|
35
|
+
}) }), _jsx(Label, { htmlFor: "bj-doc-invoice-contract", className: "cursor-pointer", children: messages.bookingCreateDialog.labels.generateInvoiceAndContract })] }), _jsxs("div", { className: "flex items-start gap-2 border-t pt-2 text-sm", children: [_jsx(Checkbox, { id: "bj-notify-traveler", checked: draft.suppressNotifications !== true, onCheckedChange: (v) => setDraft({ ...draft, suppressNotifications: v !== true }) }), _jsxs("div", { className: "flex flex-col gap-1", children: [_jsx(Label, { htmlFor: "bj-notify-traveler", className: "cursor-pointer", children: messages.bookingCreateDialog.fields.notifyTraveler }), _jsx("p", { className: "text-muted-foreground text-xs", children: messages.bookingCreateDialog.fields.notifyTravelerHint })] })] })] })] })] })] }));
|
|
36
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type Draft } from "../../lib/draft-state.js";
|
|
2
|
+
import type { PaymentProviderCapabilities, PaymentProviderStepRenderProps, VoucherPickerProps } from "../../types.js";
|
|
3
|
+
import type { StepCommonProps } from "./shared.js";
|
|
4
|
+
export declare function PaymentStep({ draft, setDraft, shape, capabilities, renderProviderStep, surface, pricing, }: StepCommonProps & {
|
|
5
|
+
capabilities: PaymentProviderCapabilities;
|
|
6
|
+
renderProviderStep?: (props: PaymentProviderStepRenderProps) => React.ReactNode;
|
|
7
|
+
surface?: "admin" | "public";
|
|
8
|
+
/** Live quote total + currency — drives the payment-schedule editor defaults. */
|
|
9
|
+
pricing?: {
|
|
10
|
+
total: number;
|
|
11
|
+
currency: string;
|
|
12
|
+
} | null;
|
|
13
|
+
}): React.ReactElement;
|
|
14
|
+
/**
|
|
15
|
+
* Operator-only PAYMENT-RELATED finalize controls — manual price override and
|
|
16
|
+
* voucher redemption (both change the amount due, so they live in the Payment
|
|
17
|
+
* block). Non-payment finalization (internal notes, document generation) lives
|
|
18
|
+
* in the separate Documents step.
|
|
19
|
+
*/
|
|
20
|
+
export declare function FinalizeControls({ draft, setDraft, pricing, renderVoucherPicker, }: {
|
|
21
|
+
draft: Draft;
|
|
22
|
+
setDraft: (next: Draft) => void;
|
|
23
|
+
pricing?: {
|
|
24
|
+
total: number;
|
|
25
|
+
currency: string;
|
|
26
|
+
} | null;
|
|
27
|
+
renderVoucherPicker?: (props: VoucherPickerProps) => React.ReactNode;
|
|
28
|
+
}): React.ReactElement;
|
|
29
|
+
//# sourceMappingURL=payment-step.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"payment-step.d.ts","sourceRoot":"","sources":["../../../../src/journey/components/journey-steps/payment-step.tsx"],"names":[],"mappings":"AAoBA,OAAO,EAAE,KAAK,KAAK,EAAc,MAAM,0BAA0B,CAAA;AAKjE,OAAO,KAAK,EACV,2BAA2B,EAC3B,8BAA8B,EAC9B,kBAAkB,EACnB,MAAM,gBAAgB,CAAA;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAMlD,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,QAAQ,EACR,KAAK,EACL,YAAY,EACZ,kBAAkB,EAClB,OAAO,EACP,OAAO,GACR,EAAE,eAAe,GAAG;IACnB,YAAY,EAAE,2BAA2B,CAAA;IACzC,kBAAkB,CAAC,EAAE,CAAC,KAAK,EAAE,8BAA8B,KAAK,KAAK,CAAC,SAAS,CAAA;IAC/E,OAAO,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAA;IAC5B,iFAAiF;IACjF,OAAO,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;CACrD,GAAG,KAAK,CAAC,YAAY,CAqIrB;AAiSD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,KAAK,EACL,QAAQ,EACR,OAAO,EACP,mBAAmB,GACpB,EAAE;IACD,KAAK,EAAE,KAAK,CAAA;IACZ,QAAQ,EAAE,CAAC,IAAI,EAAE,KAAK,KAAK,IAAI,CAAA;IAC/B,OAAO,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;IACpD,mBAAmB,CAAC,EAAE,CAAC,KAAK,EAAE,kBAAkB,KAAK,KAAK,CAAC,SAAS,CAAA;CACrE,GAAG,KAAK,CAAC,YAAY,CAerB"}
|