@voyantjs/bookings-ui 0.81.18 → 0.81.19

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.
@@ -1,5 +1,32 @@
1
+ import { type LucideIcon } from "lucide-react";
2
+ import * as React from "react";
1
3
  export interface BookingActivityTimelineProps {
2
4
  bookingId: string;
5
+ /**
6
+ * Extra events to merge into the timeline alongside the built-in
7
+ * activity / document / payment sources. Operator templates pass
8
+ * action-ledger entries through here so the timeline stays a single
9
+ * chronological feed instead of getting split across tabs.
10
+ */
11
+ additionalEvents?: readonly TimelineEvent[];
12
+ /** Rendered below the pagination — e.g. a "load more" pager for action-ledger entries. */
13
+ footer?: React.ReactNode;
14
+ /** Page size for the client-side pager. Defaults to 10. */
15
+ pageSize?: number;
3
16
  }
4
- export declare function BookingActivityTimeline({ bookingId }: BookingActivityTimelineProps): import("react/jsx-runtime").JSX.Element;
17
+ export type TimelineSource = "activity" | "document" | "payment" | "action";
18
+ export type TimelineEvent = {
19
+ id: string;
20
+ source: TimelineSource;
21
+ title: string;
22
+ description?: string | null;
23
+ actorId?: string | null;
24
+ timestamp: string;
25
+ icon: LucideIcon;
26
+ link?: {
27
+ href: string;
28
+ label: string;
29
+ };
30
+ };
31
+ export declare function BookingActivityTimeline({ bookingId, additionalEvents, footer, pageSize, }: BookingActivityTimelineProps): import("react/jsx-runtime").JSX.Element;
5
32
  //# sourceMappingURL=booking-activity-timeline.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"booking-activity-timeline.d.ts","sourceRoot":"","sources":["../../src/components/booking-activity-timeline.tsx"],"names":[],"mappings":"AAyBA,MAAM,WAAW,4BAA4B;IAC3C,SAAS,EAAE,MAAM,CAAA;CAClB;AAyCD,wBAAgB,uBAAuB,CAAC,EAAE,SAAS,EAAE,EAAE,4BAA4B,2CAgHlF"}
1
+ {"version":3,"file":"booking-activity-timeline.d.ts","sourceRoot":"","sources":["../../src/components/booking-activity-timeline.tsx"],"names":[],"mappings":"AAaA,OAAO,EAML,KAAK,UAAU,EAKhB,MAAM,cAAc,CAAA;AACrB,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAQ9B,MAAM,WAAW,4BAA4B;IAC3C,SAAS,EAAE,MAAM,CAAA;IACjB;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,SAAS,aAAa,EAAE,CAAA;IAC3C,0FAA0F;IAC1F,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IACxB,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,MAAM,cAAc,GAAG,UAAU,GAAG,UAAU,GAAG,SAAS,GAAG,QAAQ,CAAA;AAE3E,MAAM,MAAM,aAAa,GAAG;IAC1B,EAAE,EAAE,MAAM,CAAA;IACV,MAAM,EAAE,cAAc,CAAA;IACtB,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,UAAU,CAAA;IAChB,IAAI,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAA;CACvC,CAAA;AA6BD,wBAAgB,uBAAuB,CAAC,EACtC,SAAS,EACT,gBAAgB,EAChB,MAAM,EACN,QAAa,GACd,EAAE,4BAA4B,2CAgJ9B"}
@@ -2,7 +2,8 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useBookingActivity, useBookingTravelerDocuments } from "@voyantjs/bookings-react";
4
4
  import { usePublicBookingPayments } from "@voyantjs/finance-react";
5
- import { Badge, Button, Card, CardContent, CardHeader, CardTitle } from "@voyantjs/ui/components";
5
+ import { Badge, Button } from "@voyantjs/ui/components";
6
+ import { Pagination, PaginationContent, PaginationItem, PaginationLink, PaginationNext, PaginationPrevious, } from "@voyantjs/ui/components/pagination";
6
7
  import { Activity, Clock, CreditCard, ExternalLink, FileText, Pencil, Plus, RefreshCw, UserPlus, } from "lucide-react";
7
8
  import * as React from "react";
8
9
  import { formatMessage, useBookingsUiI18nOrDefault, useBookingsUiMessagesOrDefault, } from "../i18n/provider.js";
@@ -27,9 +28,11 @@ const sourceVariant = {
27
28
  activity: "outline",
28
29
  document: "secondary",
29
30
  payment: "default",
31
+ action: "secondary",
30
32
  };
31
- export function BookingActivityTimeline({ bookingId }) {
33
+ export function BookingActivityTimeline({ bookingId, additionalEvents, footer, pageSize = 10, }) {
32
34
  const [filter, setFilter] = React.useState("all");
35
+ const [pageIndex, setPageIndex] = React.useState(0);
33
36
  const { data: activityData } = useBookingActivity(bookingId);
34
37
  const { data: documentsData } = useBookingTravelerDocuments(bookingId);
35
38
  const { data: paymentsData } = usePublicBookingPayments(bookingId);
@@ -39,6 +42,7 @@ export function BookingActivityTimeline({ bookingId }) {
39
42
  activity: messages.bookingActivityTimeline.sourceLabels.activity,
40
43
  document: messages.bookingActivityTimeline.sourceLabels.document,
41
44
  payment: messages.bookingActivityTimeline.sourceLabels.payment,
45
+ action: messages.bookingActivityTimeline.sourceLabels.action,
42
46
  };
43
47
  const events = React.useMemo(() => {
44
48
  const merged = [];
@@ -48,7 +52,9 @@ export function BookingActivityTimeline({ bookingId }) {
48
52
  source: "activity",
49
53
  title: messages.bookingActivityTimeline.activityTitles[entry.activityType] ?? entry.description,
50
54
  description: entry.description,
51
- actorId: entry.actorId,
55
+ // Prefer the hydrated display name; fall back to email then raw
56
+ // id (system actors stay null and skip the "By …" render).
57
+ actorId: entry.actorName || entry.actorEmail || entry.actorId,
52
58
  timestamp: entry.createdAt,
53
59
  icon: activityIcons[entry.activityType] ?? Activity,
54
60
  });
@@ -83,12 +89,50 @@ export function BookingActivityTimeline({ bookingId }) {
83
89
  icon: CreditCard,
84
90
  });
85
91
  }
92
+ for (const extra of additionalEvents ?? []) {
93
+ merged.push(extra);
94
+ }
86
95
  merged.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
87
96
  return merged;
88
- }, [activityData, documentsData, formatNumber, messages, paymentsData]);
97
+ }, [activityData, additionalEvents, documentsData, formatNumber, messages, paymentsData]);
89
98
  const visible = filter === "all" ? events : events.filter((e) => e.source === filter);
90
- const filterChips = ["all", "activity", "document", "payment"];
91
- return (_jsxs(Card, { "data-slot": "booking-activity-timeline", children: [_jsxs(CardHeader, { className: "flex flex-row items-center justify-between", children: [_jsxs(CardTitle, { className: "flex items-center gap-2", children: [_jsx(Activity, { className: "h-4 w-4" }), messages.bookingActivityTimeline.title] }), _jsx("div", { className: "flex items-center gap-1", children: filterChips.map((chip) => (_jsx(Button, { variant: filter === chip ? "default" : "ghost", size: "sm", className: "h-7 capitalize", onClick: () => setFilter(chip), children: chip === "all" ? messages.bookingActivityTimeline.filters.all : sourceLabel[chip] }, chip))) })] }), _jsx(CardContent, { children: visible.length === 0 ? (_jsx("p", { className: "py-4 text-center text-sm text-muted-foreground", children: messages.bookingActivityTimeline.empty })) : (_jsx("div", { className: "flex flex-col gap-3", children: visible.map((event) => (_jsx(TimelineEventItem, { event: event, sourceLabel: sourceLabel }, event.id))) })) })] }));
99
+ const pageCount = Math.max(1, Math.ceil(visible.length / pageSize));
100
+ const safePageIndex = Math.min(pageIndex, pageCount - 1);
101
+ const pagedVisible = visible.slice(safePageIndex * pageSize, safePageIndex * pageSize + pageSize);
102
+ // Reset to page 1 whenever the filter changes. React's recommended
103
+ // pattern for derived state reset — cheaper and lint-clean compared
104
+ // to a useEffect.
105
+ const [lastFilter, setLastFilter] = React.useState(filter);
106
+ if (filter !== lastFilter) {
107
+ setLastFilter(filter);
108
+ setPageIndex(0);
109
+ }
110
+ const hasActionEvents = (additionalEvents?.length ?? 0) > 0;
111
+ const filterChips = hasActionEvents
112
+ ? ["all", "activity", "document", "payment", "action"]
113
+ : ["all", "activity", "document", "payment"];
114
+ return (_jsxs("div", { "data-slot": "booking-activity-timeline", className: "flex flex-col gap-3", children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [_jsxs("h2", { className: "flex items-center gap-2 text-base font-semibold", children: [_jsx(Activity, { className: "h-4 w-4 text-muted-foreground" }), messages.bookingActivityTimeline.title] }), _jsx("div", { className: "flex flex-wrap items-center gap-1", children: filterChips.map((chip) => (_jsx(Button, { variant: filter === chip ? "default" : "ghost", size: "sm", className: "h-7 capitalize", onClick: () => setFilter(chip), children: chip === "all" ? messages.bookingActivityTimeline.filters.all : sourceLabel[chip] }, chip))) })] }), visible.length === 0 ? (_jsx("div", { className: "rounded-md border bg-background p-6 text-center", children: _jsx("p", { className: "text-sm text-muted-foreground", children: messages.bookingActivityTimeline.empty }) })) : (_jsx("div", { className: "flex flex-col gap-2", children: pagedVisible.map((event) => (_jsx(TimelineEventItem, { event: event, sourceLabel: sourceLabel }, event.id))) })), pageCount > 1 ? (_jsx(TimelinePagination, { pageIndex: safePageIndex, pageCount: pageCount, onPageChange: setPageIndex })) : null, footer ? _jsx("div", { className: "mt-1 flex justify-center", children: footer }) : null] }));
115
+ }
116
+ function TimelinePagination({ pageIndex, pageCount, onPageChange, }) {
117
+ const canPrev = pageIndex > 0;
118
+ const canNext = pageIndex + 1 < pageCount;
119
+ // Compact, ellipsis-less list — the timeline rarely paginates past a
120
+ // handful of pages, and the cards are dense, so a chunky pager would
121
+ // overwhelm. If we hit > 7 pages in practice we can revisit and add
122
+ // PaginationEllipsis.
123
+ const pages = Array.from({ length: pageCount }, (_, idx) => idx);
124
+ return (_jsx(Pagination, { className: "mt-1", children: _jsxs(PaginationContent, { children: [_jsx(PaginationItem, { children: _jsx(PaginationPrevious, { "aria-disabled": !canPrev, tabIndex: canPrev ? 0 : -1, className: canPrev ? undefined : "pointer-events-none opacity-50", onClick: (event) => {
125
+ event.preventDefault();
126
+ if (canPrev)
127
+ onPageChange(pageIndex - 1);
128
+ }, href: "#" }) }), pages.map((idx) => (_jsx(PaginationItem, { children: _jsx(PaginationLink, { isActive: idx === pageIndex, onClick: (event) => {
129
+ event.preventDefault();
130
+ onPageChange(idx);
131
+ }, href: "#", children: idx + 1 }) }, idx))), _jsx(PaginationItem, { children: _jsx(PaginationNext, { "aria-disabled": !canNext, tabIndex: canNext ? 0 : -1, className: canNext ? undefined : "pointer-events-none opacity-50", onClick: (event) => {
132
+ event.preventDefault();
133
+ if (canNext)
134
+ onPageChange(pageIndex + 1);
135
+ }, href: "#" }) })] }) }));
92
136
  }
93
137
  function TimelineEventItem({ event, sourceLabel, }) {
94
138
  const Icon = event.icon;
@@ -1,5 +1,6 @@
1
1
  import { type BookingRecord } from "@voyantjs/bookings-react";
2
2
  import { type ReactNode } from "react";
3
+ import { type TimelineEvent } from "./booking-activity-timeline.js";
3
4
  import { type BookingItemResourceKind } from "./booking-item-list.js";
4
5
  import { type BookingPaymentsSummaryRow } from "./booking-payments-summary.js";
5
6
  /**
@@ -30,9 +31,17 @@ export interface BookingDetailPageSlots {
30
31
  activityEnd?: (booking: BookingRecord) => ReactNode;
31
32
  /** Mounts a dedicated `Invoices` tab between Payments and Suppliers. */
32
33
  invoicesTab?: BookingDetailTabSlot;
33
- /** Mounts a dedicated `Ledger` tab at the far right. */
34
- ledgerTab?: BookingDetailTabSlot;
34
+ /**
35
+ * Extra events merged into the Activity-tab timeline. Operator
36
+ * templates pass action-ledger entries here so the timeline stays a
37
+ * single chronological feed.
38
+ */
39
+ activityExtraEvents?: readonly TimelineEvent[];
40
+ /** Rendered below the activity timeline events — typically a "load more" pager. */
41
+ activityTimelineFooter?: ReactNode;
35
42
  }
43
+ /** Tab values used by the canonical `BookingDetailPage`. */
44
+ export type BookingDetailTabValue = "items" | "travelers" | "finance" | "invoices" | "documents" | "suppliers" | "activity" | "metadata";
36
45
  export interface BookingDetailPageProps {
37
46
  id: string;
38
47
  className?: string;
@@ -86,9 +95,19 @@ export interface BookingDetailPageProps {
86
95
  onEditPayment?: (row: BookingPaymentsSummaryRow) => void;
87
96
  /** Forwarded to the finance-tab `BookingPaymentsSummary` row menu. */
88
97
  onDeletePayment?: (row: BookingPaymentsSummaryRow) => Promise<void> | void;
98
+ /**
99
+ * Controlled active-tab value. Hosts wire this to their router so
100
+ * the active tab can be reflected in the URL (`?tab=activity` etc.)
101
+ * and the page reloads to the right tab when a link is shared.
102
+ * Leave both `activeTab` + `onTabChange` undefined for uncontrolled
103
+ * mode (defaults to `overview`).
104
+ */
105
+ activeTab?: BookingDetailTabValue;
106
+ /** Fires when the user switches tabs. Hosts push the new value into the URL. */
107
+ onTabChange?: (tab: BookingDetailTabValue) => void;
89
108
  slots?: BookingDetailPageSlots;
90
109
  }
91
- export declare function BookingDetailPage({ id, className, locale, hideBreadcrumb, onBack, onRecordPayment, recordPaymentDisabledReason, addScheduleDisabledReason, paidAmountCents, onItemResourceOpen, onPersonOpen, onOrganizationOpen, onInvoiceOpen, onViewPayment, onEditPayment, onDeletePayment, slots, }: BookingDetailPageProps): import("react/jsx-runtime").JSX.Element;
110
+ export declare function BookingDetailPage({ id, className, locale, hideBreadcrumb, onBack, onRecordPayment, recordPaymentDisabledReason, addScheduleDisabledReason, paidAmountCents, onItemResourceOpen, onPersonOpen, onOrganizationOpen, onInvoiceOpen, onViewPayment, onEditPayment, onDeletePayment, activeTab, onTabChange, slots, }: BookingDetailPageProps): import("react/jsx-runtime").JSX.Element;
92
111
  /**
93
112
  * Billing/contact card for the Travelers tab. Snapshot fields on the
94
113
  * booking row are the source of truth (they capture contact info at
@@ -1 +1 @@
1
- {"version":3,"file":"booking-detail-page.d.ts","sourceRoot":"","sources":["../../src/components/booking-detail-page.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"booking-detail-page.d.ts","sourceRoot":"","sources":["../../src/components/booking-detail-page.tsx"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,aAAa,EAInB,MAAM,0BAA0B,CAAA;AAsCjC,OAAO,EAAY,KAAK,SAAS,EAAY,MAAM,OAAO,CAAA;AAO1D,OAAO,EAA2B,KAAK,aAAa,EAAE,MAAM,gCAAgC,CAAA;AAM5F,OAAO,EAAmB,KAAK,uBAAuB,EAAE,MAAM,wBAAwB,CAAA;AAGtF,OAAO,EAEL,KAAK,yBAAyB,EAC/B,MAAM,+BAA+B,CAAA;AAMtC;;;;;GAKG;AACH,MAAM,WAAW,oBAAoB;IACnC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,6EAA6E;IAC7E,OAAO,EAAE,wBAAwB,CAAA;CAClC;AAED,MAAM,MAAM,wBAAwB,GAAG,SAAS,GAAG,CAAC,CAAC,OAAO,EAAE,aAAa,KAAK,SAAS,CAAC,CAAA;AAE1F,MAAM,WAAW,sBAAsB;IACrC,2DAA2D;IAC3D,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,SAAS,CAAA;IAC9C,sDAAsD;IACtD,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,SAAS,CAAA;IACpD,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,SAAS,CAAA;IACrD,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,SAAS,CAAA;IACnD,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,SAAS,CAAA;IACtD,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,SAAS,CAAA;IACpD,sEAAsE;IACtE,eAAe,CAAC,EAAE,wBAAwB,CAAA;IAC1C,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,SAAS,CAAA;IAClD,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,SAAS,CAAA;IACjD,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,SAAS,CAAA;IACnD,wEAAwE;IACxE,WAAW,CAAC,EAAE,oBAAoB,CAAA;IAClC;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,SAAS,aAAa,EAAE,CAAA;IAC9C,mFAAmF;IACnF,sBAAsB,CAAC,EAAE,SAAS,CAAA;CACnC;AAED,4DAA4D;AAC5D,MAAM,MAAM,qBAAqB,GAC7B,OAAO,GACP,WAAW,GACX,SAAS,GACT,UAAU,GACV,WAAW,GACX,WAAW,GACX,UAAU,GACV,UAAU,CAAA;AAEd,MAAM,WAAW,sBAAsB;IACrC,EAAE,EAAE,MAAM,CAAA;IACV,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;gEAC4D;IAC5D,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAA;IACnB;;;OAGG;IACH,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,IAAI,CAAA;IAClD;;;OAGG;IACH,2BAA2B,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3C;;;OAGG;IACH,yBAAyB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACzC;;;;;OAKG;IACH,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,CAAC,IAAI,EAAE,uBAAuB,EAAE,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IACxE,yEAAyE;IACzE,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAA;IACzC,+EAA+E;IAC/E,kBAAkB,CAAC,EAAE,CAAC,cAAc,EAAE,MAAM,KAAK,IAAI,CAAA;IACrD;;;;;OAKG;IACH,aAAa,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,yBAAyB,KAAK,IAAI,CAAA;IAC3E,sEAAsE;IACtE,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,yBAAyB,KAAK,IAAI,CAAA;IACxD,sEAAsE;IACtE,aAAa,CAAC,EAAE,CAAC,GAAG,EAAE,yBAAyB,KAAK,IAAI,CAAA;IACxD,sEAAsE;IACtE,eAAe,CAAC,EAAE,CAAC,GAAG,EAAE,yBAAyB,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;IAC1E;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,qBAAqB,CAAA;IACjC,gFAAgF;IAChF,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,qBAAqB,KAAK,IAAI,CAAA;IAClD,KAAK,CAAC,EAAE,sBAAsB,CAAA;CAC/B;AAED,wBAAgB,iBAAiB,CAAC,EAChC,EAAE,EACF,SAAS,EACT,MAAM,EACN,cAAc,EACd,MAAM,EACN,eAAe,EACf,2BAA2B,EAC3B,yBAAyB,EACzB,eAAe,EACf,kBAAkB,EAClB,YAAY,EACZ,kBAAkB,EAClB,aAAa,EACb,aAAa,EACb,aAAa,EACb,eAAe,EACf,SAAS,EACT,WAAW,EACX,KAAK,GACN,EAAE,sBAAsB,2CAmbxB;AAqCD;;;;;;;GAOG;AACH,wBAAgB,yBAAyB,CAAC,EACxC,OAAO,EACP,YAAY,EACZ,kBAAkB,GACnB,EAAE;IACD,OAAO,EAAE,aAAa,CAAA;IACtB,gDAAgD;IAChD,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAA;IACzC,sDAAsD;IACtD,kBAAkB,CAAC,EAAE,CAAC,cAAc,EAAE,MAAM,KAAK,IAAI,CAAA;CACtD,2CA0FA"}
@@ -6,7 +6,7 @@ import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent,
6
6
  import { Collapsible, CollapsibleContent, CollapsibleTrigger, } from "@voyantjs/ui/components/collapsible";
7
7
  import { Tabs, TabsContent, TabsList, TabsTrigger } from "@voyantjs/ui/components/tabs";
8
8
  import { Tooltip, TooltipContent, TooltipTrigger } from "@voyantjs/ui/components/tooltip";
9
- import { Ban, ChevronDown, ChevronRight, CreditCard, Mail, MapPin, Pencil, Phone, Plus, RefreshCw, Trash2, } from "lucide-react";
9
+ import { Ban, ChevronDown, ChevronRight, CreditCard, Info, Mail, MapPin, Pencil, Phone, Plus, RefreshCw, Trash2, } from "lucide-react";
10
10
  import { Fragment, useState } from "react";
11
11
  import { formatMessage, useBookingsUiI18nOrDefault, useBookingsUiMessagesOrDefault, } from "../i18n/index.js";
12
12
  import { BookingActivityTimeline } from "./booking-activity-timeline.js";
@@ -23,7 +23,7 @@ import { StatusBadge } from "./status-badge.js";
23
23
  import { StatusChangeDialog } from "./status-change-dialog.js";
24
24
  import { SupplierStatusList } from "./supplier-status-list.js";
25
25
  import { TravelerList } from "./traveler-list.js";
26
- export function BookingDetailPage({ id, className, locale, hideBreadcrumb, onBack, onRecordPayment, recordPaymentDisabledReason, addScheduleDisabledReason, paidAmountCents, onItemResourceOpen, onPersonOpen, onOrganizationOpen, onInvoiceOpen, onViewPayment, onEditPayment, onDeletePayment, slots, }) {
26
+ export function BookingDetailPage({ id, className, locale, hideBreadcrumb, onBack, onRecordPayment, recordPaymentDisabledReason, addScheduleDisabledReason, paidAmountCents, onItemResourceOpen, onPersonOpen, onOrganizationOpen, onInvoiceOpen, onViewPayment, onEditPayment, onDeletePayment, activeTab, onTabChange, slots, }) {
27
27
  const i18n = useBookingsUiI18nOrDefault();
28
28
  const messages = useBookingsUiMessagesOrDefault();
29
29
  const detailMessages = messages.bookingDetailPage;
@@ -91,7 +91,7 @@ export function BookingDetailPage({ id, className, locale, hideBreadcrumb, onBac
91
91
  ? formatAmount(paidAmountCents, booking.sellCurrency, resolvedLocale, detailMessages.noValue)
92
92
  : detailMessages.noValue }), _jsx(StatCard, { label: detailMessages.summaryCostMargin, badge: booking.marginPercent != null
93
93
  ? renderPercentBadge(booking.marginPercent, marginBadgeClass)
94
- : null, children: formatAmount(booking.costAmountCents, booking.sellCurrency, resolvedLocale, detailMessages.noValue) }), _jsx(StatCard, { label: detailMessages.summaryCreated, children: formatDate(booking.createdAt, resolvedLocale, detailMessages.noValue) })] }), slots?.afterSummary?.(booking), _jsxs(Tabs, { defaultValue: "overview", children: [_jsxs(TabsList, { className: "w-full justify-start", children: [_jsx(TabsTrigger, { value: "overview", children: detailMessages.tabOverview }), _jsx(TabsTrigger, { value: "travelers", children: detailMessages.tabTravelers }), _jsx(TabsTrigger, { value: "finance", children: detailMessages.tabFinance }), slots?.invoicesTab ? (_jsx(TabsTrigger, { value: "invoices", children: slots.invoicesTab.label ?? detailMessages.tabInvoices })) : null, _jsx(TabsTrigger, { value: "documents", children: detailMessages.tabDocuments }), _jsx(TabsTrigger, { value: "suppliers", children: detailMessages.tabSuppliers }), _jsx(TabsTrigger, { value: "activity", children: detailMessages.tabActivity }), slots?.ledgerTab ? (_jsx(TabsTrigger, { value: "ledger", children: slots.ledgerTab.label ?? detailMessages.tabLedger })) : null, _jsx(TabsTrigger, { value: "meta", children: detailMessages.tabMeta })] }), _jsxs(TabsContent, { value: "overview", className: "mt-4 flex flex-col gap-6", children: [slots?.overviewStart?.(booking), _jsx(BookingItemList, { bookingId: id, onResourceOpen: onItemResourceOpen }), _jsx(BookingGroupSection, { bookingId: id }), visibleInternalNotes(booking.internalNotes) ? (_jsx(Card, { children: _jsxs(CardContent, { className: "py-5", children: [_jsx("p", { className: "mb-1 text-xs font-medium text-muted-foreground", children: detailMessages.internalNotesLabel }), _jsx("p", { className: "whitespace-pre-wrap text-sm", children: visibleInternalNotes(booking.internalNotes) })] }) })) : null, slots?.overviewEnd?.(booking)] }), _jsxs(TabsContent, { value: "travelers", className: "mt-4 flex flex-col gap-6", children: [slots?.travelersStart?.(booking), _jsx(BookingBillingContextCard, { booking: booking, onPersonOpen: onPersonOpen, onOrganizationOpen: onOrganizationOpen }), _jsx(TravelerList, { bookingId: id, autoReveal: true })] }), _jsxs(TabsContent, { value: "finance", className: "mt-4 flex flex-col gap-6", children: [_jsx(BookingPaymentScheduleList, { bookingId: id, addScheduleDisabledReason: addScheduleDisabledReason ?? null }), slots?.paymentsContent ? (renderDetailSlot(slots.paymentsContent, booking)) : (_jsx(BookingPaymentsSummary, { bookingId: id, variant: "admin", onViewPayment: onViewPayment, onInvoiceOpen: onInvoiceOpen, onEditPayment: onEditPayment, onDeletePayment: onDeletePayment, headerAction: onRecordPayment ? (_jsx(RecordPaymentHeaderButton, { label: detailMessages.recordPaymentAction, disabledReason: recordPaymentDisabledReason ?? null, onClick: () => onRecordPayment(booking) })) : null })), slots?.financeStart?.(booking), _jsxs(Collapsible, { children: [_jsxs(CollapsibleTrigger, { className: "group flex w-full items-center justify-between rounded-md border bg-background px-4 py-3 text-sm font-semibold hover:bg-muted/30", children: [messages.bookingGuaranteeList.title, _jsx(ChevronDown, { className: "h-4 w-4 transition-transform group-data-panel-open:rotate-180" })] }), _jsx(CollapsibleContent, { className: "mt-3", children: _jsx(BookingGuaranteeList, { bookingId: id }) })] }), slots?.financeEnd?.(booking)] }), slots?.invoicesTab ? (_jsx(TabsContent, { value: "invoices", className: "mt-4 flex flex-col gap-6", children: renderDetailSlot(slots.invoicesTab.content, booking) })) : null, _jsx(TabsContent, { value: "documents", className: "mt-4 flex flex-col gap-4", children: slots?.documents ? (slots.documents(booking)) : (_jsx("p", { className: "rounded-md border border-dashed p-4 text-sm text-muted-foreground", children: detailMessages.documentsSlotEmpty })) }), _jsx(TabsContent, { value: "suppliers", className: "mt-4", children: _jsx(SupplierStatusList, { bookingId: id }) }), _jsxs(TabsContent, { value: "activity", className: "mt-4 flex flex-col gap-6", children: [_jsx(BookingActivityTimeline, { bookingId: id }), _jsx(BookingNotes, { bookingId: id }), slots?.activityEnd?.(booking)] }), slots?.ledgerTab ? (_jsx(TabsContent, { value: "ledger", className: "mt-4 flex flex-col gap-6", children: renderDetailSlot(slots.ledgerTab.content, booking) })) : null, _jsx(TabsContent, { value: "meta", className: "mt-4", children: _jsx(Card, { children: _jsx(CardContent, { className: "grid grid-cols-2 gap-6 py-6 sm:grid-cols-4", children: _jsx(SummaryStat, { label: detailMessages.summaryUpdated, value: formatDate(booking.updatedAt, resolvedLocale, detailMessages.noValue) }) }) }) })] }), _jsx(BookingDialog, { open: editOpen, onOpenChange: setEditOpen, booking: booking }), _jsx(StatusChangeDialog, { open: statusDialogOpen, onOpenChange: setStatusDialogOpen, bookingId: id, currentStatus: booking.status }), _jsx(BookingCancellationDialog, { open: cancelDialogOpen, onOpenChange: setCancelDialogOpen, booking: booking }), _jsx(AlertDialog, { open: deleteDialogOpen, onOpenChange: (next) => {
94
+ : null, children: formatAmount(booking.costAmountCents, booking.sellCurrency, resolvedLocale, detailMessages.noValue) }), _jsx(StatCard, { label: detailMessages.summaryCreated, children: formatDate(booking.createdAt, resolvedLocale, detailMessages.noValue) })] }), slots?.afterSummary?.(booking), _jsxs(Tabs, { defaultValue: "items", value: activeTab, onValueChange: (value) => onTabChange?.(String(value)), children: [_jsxs(TabsList, { className: "w-full justify-start", children: [_jsx(TabsTrigger, { value: "items", children: detailMessages.tabOverview }), _jsx(TabsTrigger, { value: "travelers", children: detailMessages.tabTravelers }), _jsx(TabsTrigger, { value: "finance", children: detailMessages.tabFinance }), slots?.invoicesTab ? (_jsx(TabsTrigger, { value: "invoices", children: slots.invoicesTab.label ?? detailMessages.tabInvoices })) : null, _jsx(TabsTrigger, { value: "documents", children: detailMessages.tabDocuments }), _jsx(TabsTrigger, { value: "suppliers", children: detailMessages.tabSuppliers }), _jsx(TabsTrigger, { value: "activity", children: detailMessages.tabActivity }), _jsx(TabsTrigger, { value: "metadata", children: detailMessages.tabMetadata })] }), _jsxs(TabsContent, { value: "items", className: "mt-4 flex flex-col gap-6", children: [slots?.overviewStart?.(booking), _jsx(BookingItemList, { bookingId: id, onResourceOpen: onItemResourceOpen }), _jsx(BookingGroupSection, { bookingId: id }), visibleInternalNotes(booking.internalNotes) ? (_jsx(Card, { children: _jsxs(CardContent, { className: "py-5", children: [_jsx("p", { className: "mb-1 text-xs font-medium text-muted-foreground", children: detailMessages.internalNotesLabel }), _jsx("p", { className: "whitespace-pre-wrap text-sm", children: visibleInternalNotes(booking.internalNotes) })] }) })) : null, slots?.overviewEnd?.(booking)] }), _jsxs(TabsContent, { value: "travelers", className: "mt-4 flex flex-col gap-6", children: [slots?.travelersStart?.(booking), _jsx(BookingBillingContextCard, { booking: booking, onPersonOpen: onPersonOpen, onOrganizationOpen: onOrganizationOpen }), _jsx(TravelerList, { bookingId: id, autoReveal: true })] }), _jsxs(TabsContent, { value: "finance", className: "mt-4 flex flex-col gap-6", children: [_jsx(BookingPaymentScheduleList, { bookingId: id, addScheduleDisabledReason: addScheduleDisabledReason ?? null }), slots?.paymentsContent ? (renderDetailSlot(slots.paymentsContent, booking)) : (_jsx(BookingPaymentsSummary, { bookingId: id, variant: "admin", onViewPayment: onViewPayment, onInvoiceOpen: onInvoiceOpen, onEditPayment: onEditPayment, onDeletePayment: onDeletePayment, headerAction: onRecordPayment ? (_jsx(RecordPaymentHeaderButton, { label: detailMessages.recordPaymentAction, disabledReason: recordPaymentDisabledReason ?? null, onClick: () => onRecordPayment(booking) })) : null })), slots?.financeStart?.(booking), _jsxs(Collapsible, { children: [_jsxs(CollapsibleTrigger, { className: "group flex w-full items-center justify-between rounded-md border bg-background px-4 py-3 text-sm font-semibold hover:bg-muted/30", children: [messages.bookingGuaranteeList.title, _jsx(ChevronDown, { className: "h-4 w-4 transition-transform group-data-panel-open:rotate-180" })] }), _jsx(CollapsibleContent, { className: "mt-3", children: _jsx(BookingGuaranteeList, { bookingId: id }) })] }), slots?.financeEnd?.(booking)] }), slots?.invoicesTab ? (_jsx(TabsContent, { value: "invoices", className: "mt-4 flex flex-col gap-6", children: renderDetailSlot(slots.invoicesTab.content, booking) })) : null, _jsx(TabsContent, { value: "documents", className: "mt-4 flex flex-col gap-4", children: slots?.documents ? (slots.documents(booking)) : (_jsx("p", { className: "rounded-md border border-dashed p-4 text-sm text-muted-foreground", children: detailMessages.documentsSlotEmpty })) }), _jsx(TabsContent, { value: "suppliers", className: "mt-4", children: _jsx(SupplierStatusList, { bookingId: id }) }), _jsxs(TabsContent, { value: "activity", className: "mt-4 flex flex-col gap-6", children: [_jsx(BookingNotes, { bookingId: id }), _jsx(BookingActivityTimeline, { bookingId: id, additionalEvents: slots?.activityExtraEvents, footer: slots?.activityTimelineFooter }), slots?.activityEnd?.(booking)] }), _jsx(TabsContent, { value: "metadata", className: "mt-4", children: _jsx(BookingMetadataList, { booking: booking, messages: detailMessages.metadataSection, statusLabel: getBookingStatusLabel(booking.status, messages.common.bookingStatusLabels), formatDateTime: i18n.formatDateTime, noValue: detailMessages.noValue }) })] }), _jsx(BookingDialog, { open: editOpen, onOpenChange: setEditOpen, booking: booking }), _jsx(StatusChangeDialog, { open: statusDialogOpen, onOpenChange: setStatusDialogOpen, bookingId: id, currentStatus: booking.status }), _jsx(BookingCancellationDialog, { open: cancelDialogOpen, onOpenChange: setCancelDialogOpen, booking: booking }), _jsx(AlertDialog, { open: deleteDialogOpen, onOpenChange: (next) => {
95
95
  if (!next && !remove.isPending)
96
96
  setDeleteDialogOpen(false);
97
97
  }, children: _jsxs(AlertDialogContent, { size: "sm", children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: detailMessages.deleteConfirm }), _jsx(AlertDialogDescription, { children: booking.bookingNumber
@@ -148,12 +148,39 @@ export function BookingBillingContextCard({ booking, onPersonOpen, onOrganizatio
148
148
  .join(", ");
149
149
  return (_jsxs("div", { "data-slot": "booking-billing-context", className: "flex flex-col gap-3", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("h2", { className: "flex items-center gap-2 text-base font-semibold", children: [_jsx(CreditCard, { className: "h-4 w-4", "aria-hidden": "true" }), messages.billingPayer] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: () => setEditOpen(true), children: [_jsx(Pencil, { className: "mr-1 h-3.5 w-3.5", "aria-hidden": "true" }), messages.editAction] })] }), _jsxs("div", { className: "flex flex-col gap-4 rounded-md border p-4", children: [_jsxs("div", { className: "grid gap-4 md:grid-cols-3", children: [_jsx(BillingField, { label: messages.billingPayer, value: payerName ? (booking.personId && onPersonOpen ? (_jsx("button", { type: "button", onClick: () => onPersonOpen(booking.personId), className: "text-left text-primary hover:underline", children: payerName })) : booking.organizationId && !booking.personId && onOrganizationOpen ? (_jsx("button", { type: "button", onClick: () => onOrganizationOpen(booking.organizationId), className: "text-left text-primary hover:underline", children: payerName })) : (payerName)) : (messages.noValue) }), _jsx(BillingField, { label: messages.billingEmail, value: email ?? messages.noValue, icon: _jsx(Mail, { className: "h-3.5 w-3.5", "aria-hidden": "true" }) }), _jsx(BillingField, { label: messages.billingPhone, value: phone ?? messages.noValue, icon: _jsx(Phone, { className: "h-3.5 w-3.5", "aria-hidden": "true" }) })] }), _jsx(BillingField, { label: messages.billingAddress, value: address || messages.noValue, icon: _jsx(MapPin, { className: "h-3.5 w-3.5", "aria-hidden": "true" }) })] }), _jsx(BookingBillingDialog, { open: editOpen, onOpenChange: setEditOpen, booking: booking })] }));
150
150
  }
151
- function SummaryStat({ label, value, hint, icon, }) {
152
- return (_jsxs("div", { className: "flex flex-col gap-1", children: [_jsxs("div", { className: "flex items-center gap-1.5 text-xs font-medium text-muted-foreground", children: [icon, label] }), _jsx("div", { className: "text-base font-semibold tabular-nums", children: value }), hint ? _jsx("div", { className: "text-xs text-muted-foreground", children: hint }) : null] }));
153
- }
154
151
  function StatCard({ label, children, hint, badge, }) {
155
152
  return (_jsx(Card, { children: _jsxs(CardContent, { className: "flex flex-col gap-1", children: [_jsx("div", { className: "text-xs font-medium text-muted-foreground", children: label }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx("div", { className: "text-xl font-semibold tabular-nums leading-none", children: children }), badge] }), hint ? _jsx("div", { className: "text-xs text-muted-foreground", children: hint }) : null] }) }));
156
153
  }
154
+ /**
155
+ * Definition-list metadata panel for the Metadata tab. Surfaces booking
156
+ * fields that don't fit any other tab but are still useful for support
157
+ * / debugging: raw id, booking number, status, communication language,
158
+ * created/updated timestamps. Label-left, value-right rows matching the
159
+ * supplier-status / documents / notes tab style.
160
+ */
161
+ function BookingMetadataList({ booking, messages, statusLabel, formatDateTime, noValue, }) {
162
+ const rows = [
163
+ {
164
+ label: messages.bookingId,
165
+ value: _jsx("span", { className: "font-mono text-xs", children: booking.id }),
166
+ },
167
+ {
168
+ label: messages.bookingNumber,
169
+ value: _jsx("span", { className: "font-mono text-xs", children: booking.bookingNumber }),
170
+ },
171
+ {
172
+ label: messages.status,
173
+ value: _jsx(StatusBadge, { status: booking.status, children: statusLabel }),
174
+ },
175
+ {
176
+ label: messages.communicationLanguage,
177
+ value: booking.communicationLanguage ? (_jsx("span", { className: "uppercase", children: booking.communicationLanguage })) : (_jsx("span", { className: "text-muted-foreground", children: noValue })),
178
+ },
179
+ { label: messages.created, value: formatDateTime(booking.createdAt) },
180
+ { label: messages.updated, value: formatDateTime(booking.updatedAt) },
181
+ ];
182
+ return (_jsxs("div", { "data-slot": "booking-metadata", className: "flex flex-col gap-3", children: [_jsxs("h2", { className: "flex items-center gap-2 text-base font-semibold", children: [_jsx(Info, { className: "h-4 w-4 text-muted-foreground" }), messages.title] }), _jsx("div", { className: "overflow-hidden rounded-md border bg-background", children: _jsx("dl", { className: "divide-y", children: rows.map((row) => (_jsxs("div", { className: "flex items-center justify-between gap-4 px-4 py-3 text-sm", children: [_jsx("dt", { className: "text-muted-foreground", children: row.label }), _jsx("dd", { className: "text-right", children: row.value })] }, row.label))) }) })] }));
183
+ }
157
184
  /**
158
185
  * Traffic-light badge for a percentage value. Color thresholds are
159
186
  * passed in by the caller (Paid uses 0 → red, 0..100 → yellow, 100 →
@@ -1,5 +1,30 @@
1
- import { type BookingRecord } from "@voyantjs/bookings-react";
1
+ import { type BookingRecord, type BookingsListSortDir, type BookingsListSortField } from "@voyantjs/bookings-react";
2
2
  import * as React from "react";
3
+ /**
4
+ * Serializable snapshot of the booking-list filter / sort / paging
5
+ * state. Hosts that want shareable URLs (operator template) read this
6
+ * from the URL on mount via `initialFilters` and push changes via
7
+ * `onFiltersChange`.
8
+ */
9
+ export interface BookingListFiltersState {
10
+ search: string;
11
+ /** `BOOKING_STATUS_ALL` ("all") or any booking status value. */
12
+ status: string;
13
+ productId: string | null;
14
+ optionId: string | null;
15
+ supplierId: string | null;
16
+ productCategoryId: string | null;
17
+ personId: string | null;
18
+ organizationId: string | null;
19
+ availabilitySlotId: string | null;
20
+ dateFrom: string | null;
21
+ dateTo: string | null;
22
+ paxMin: string;
23
+ paxMax: string;
24
+ sortBy: BookingsListSortField;
25
+ sortDir: BookingsListSortDir;
26
+ offset: number;
27
+ }
3
28
  export interface BookingListProps {
4
29
  pageSize?: number;
5
30
  onSelectBooking?: (booking: BookingRecord) => void;
@@ -10,6 +35,16 @@ export interface BookingListProps {
10
35
  * the trip composer without forking the component.
11
36
  */
12
37
  headerActions?: React.ReactNode;
38
+ /**
39
+ * Initial filter / sort / paging state, typically parsed from the URL.
40
+ * Only specified keys override the defaults — partial input is fine.
41
+ */
42
+ initialFilters?: Partial<BookingListFiltersState>;
43
+ /**
44
+ * Fires when any filter, sort, or paging value changes. Hosts push
45
+ * the snapshot into the URL so refresh / share preserves the view.
46
+ */
47
+ onFiltersChange?: (filters: BookingListFiltersState) => void;
13
48
  }
14
- export declare function BookingList({ pageSize, onSelectBooking, onCreateBooking, headerActions, }?: BookingListProps): import("react/jsx-runtime").JSX.Element;
49
+ export declare function BookingList({ pageSize, onSelectBooking, onCreateBooking, headerActions, initialFilters, onFiltersChange, }?: BookingListProps): import("react/jsx-runtime").JSX.Element;
15
50
  //# sourceMappingURL=booking-list.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"booking-list.d.ts","sourceRoot":"","sources":["../../src/components/booking-list.tsx"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,aAAa,EAInB,MAAM,0BAA0B,CAAA;AAcjC,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAU9B,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,IAAI,CAAA;IAClD,eAAe,CAAC,EAAE,MAAM,IAAI,CAAA;IAC5B;;;;OAIG;IACH,aAAa,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;CAChC;AAiBD,wBAAgB,WAAW,CAAC,EAC1B,QAAa,EACb,eAAe,EACf,eAAe,EACf,aAAa,GACd,GAAE,gBAAqB,2CAoWvB"}
1
+ {"version":3,"file":"booking-list.d.ts","sourceRoot":"","sources":["../../src/components/booking-list.tsx"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACxB,KAAK,qBAAqB,EAE3B,MAAM,0BAA0B,CAAA;AAsBjC,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAU9B;;;;;GAKG;AACH,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,MAAM,CAAA;IACd,gEAAgE;IAChE,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAA;IAChC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAA;IACjC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,qBAAqB,CAAA;IAC7B,OAAO,EAAE,mBAAmB,CAAA;IAC5B,MAAM,EAAE,MAAM,CAAA;CACf;AA6BD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,IAAI,CAAA;IAClD,eAAe,CAAC,EAAE,MAAM,IAAI,CAAA;IAC5B;;;;OAIG;IACH,aAAa,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC/B;;;OAGG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC,uBAAuB,CAAC,CAAA;IACjD;;;OAGG;IACH,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,uBAAuB,KAAK,IAAI,CAAA;CAC7D;AAiBD,wBAAgB,WAAW,CAAC,EAC1B,QAAa,EACb,eAAe,EACf,eAAe,EACf,aAAa,EACb,cAAc,EACd,eAAe,GAChB,GAAE,gBAAqB,2CA+XvB"}