@voyantjs/products-ui 0.52.2 → 0.52.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/product-action-ledger-card.d.ts +17 -0
- package/dist/components/product-action-ledger-card.d.ts.map +1 -0
- package/dist/components/product-action-ledger-card.js +73 -0
- package/dist/components/product-detail-page.d.ts +6 -18
- package/dist/components/product-detail-page.d.ts.map +1 -1
- package/dist/components/product-detail-page.js +6 -101
- package/dist/components/product-itinerary-section.d.ts +16 -0
- package/dist/components/product-itinerary-section.d.ts.map +1 -0
- package/dist/components/product-itinerary-section.js +105 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/package.json +19 -19
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export interface ProductActionLedgerCardProps {
|
|
2
|
+
productId: string;
|
|
3
|
+
limit?: number;
|
|
4
|
+
messages?: Partial<ProductActionLedgerCardMessages>;
|
|
5
|
+
className?: string;
|
|
6
|
+
}
|
|
7
|
+
export interface ProductActionLedgerCardMessages {
|
|
8
|
+
title: string;
|
|
9
|
+
description: string;
|
|
10
|
+
loadOlder: string;
|
|
11
|
+
empty: string;
|
|
12
|
+
loadFailed: string;
|
|
13
|
+
}
|
|
14
|
+
export declare function ProductActionLedgerCard({ productId, limit, messages: messagesOverride, className, }: ProductActionLedgerCardProps): import("react/jsx-runtime").JSX.Element;
|
|
15
|
+
export declare const actionLedgerStatusVariant: Record<string, "default" | "secondary" | "outline" | "destructive">;
|
|
16
|
+
export declare const actionLedgerRiskVariant: Record<string, "default" | "secondary" | "outline" | "destructive">;
|
|
17
|
+
//# sourceMappingURL=product-action-ledger-card.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"product-action-ledger-card.d.ts","sourceRoot":"","sources":["../../src/components/product-action-ledger-card.tsx"],"names":[],"mappings":"AAeA,MAAM,WAAW,4BAA4B;IAC3C,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC,+BAA+B,CAAC,CAAA;IACnD,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,+BAA+B;IAC9C,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;IACb,UAAU,EAAE,MAAM,CAAA;CACnB;AAUD,wBAAgB,uBAAuB,CAAC,EACtC,SAAS,EACT,KAAU,EACV,QAAQ,EAAE,gBAAgB,EAC1B,SAAS,GACV,EAAE,4BAA4B,2CAiE9B;AA4ED,eAAO,MAAM,yBAAyB,EAAE,MAAM,CAC5C,MAAM,EACN,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,aAAa,CAapD,CAAA;AAED,eAAO,MAAM,uBAAuB,EAAE,MAAM,CAC1C,MAAM,EACN,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,aAAa,CAMpD,CAAA"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useProductActionLedger } from "@voyantjs/products-react";
|
|
4
|
+
import { Badge } from "@voyantjs/ui/components/badge";
|
|
5
|
+
import { Button } from "@voyantjs/ui/components/button";
|
|
6
|
+
import { cn } from "@voyantjs/ui/lib/utils";
|
|
7
|
+
import { Activity, Loader2 } from "lucide-react";
|
|
8
|
+
import { useEffect, useState } from "react";
|
|
9
|
+
import { useProductsUiI18nOrDefault } from "../i18n/provider.js";
|
|
10
|
+
const defaultProductActionLedgerCardMessages = {
|
|
11
|
+
title: "Activity", // i18n-literal-ok: package-owned fallback copy; callers can override.
|
|
12
|
+
description: "Recent product edits and the actor that made them.", // i18n-literal-ok: package-owned fallback copy; callers can override.
|
|
13
|
+
loadOlder: "Load older activity", // i18n-literal-ok: package-owned fallback copy; callers can override.
|
|
14
|
+
empty: "No product activity recorded.", // i18n-literal-ok: package-owned fallback copy; callers can override.
|
|
15
|
+
loadFailed: "Failed to load product activity.", // i18n-literal-ok: package-owned fallback copy; callers can override.
|
|
16
|
+
};
|
|
17
|
+
export function ProductActionLedgerCard({ productId, limit = 20, messages: messagesOverride, className, }) {
|
|
18
|
+
const messages = { ...defaultProductActionLedgerCardMessages, ...messagesOverride };
|
|
19
|
+
const [cursor, setCursor] = useState(null);
|
|
20
|
+
const actionLedgerQuery = useProductActionLedger(productId, { cursor, limit });
|
|
21
|
+
const { formatDateTime } = useProductsUiI18nOrDefault();
|
|
22
|
+
const [pages, setPages] = useState([]);
|
|
23
|
+
useEffect(() => {
|
|
24
|
+
const page = actionLedgerQuery.data;
|
|
25
|
+
if (!page)
|
|
26
|
+
return;
|
|
27
|
+
setPages((currentPages) => {
|
|
28
|
+
if (currentPages.some((currentPage) => currentPage.data[0]?.id === page.data[0]?.id)) {
|
|
29
|
+
return currentPages;
|
|
30
|
+
}
|
|
31
|
+
return cursor ? [...currentPages, page] : [page];
|
|
32
|
+
});
|
|
33
|
+
}, [actionLedgerQuery.data, cursor]);
|
|
34
|
+
const entries = pages.flatMap((page) => page.data);
|
|
35
|
+
const nextCursor = pages.at(-1)?.pageInfo.nextCursor ?? null;
|
|
36
|
+
return (_jsx(ActionLedgerSection, { title: messages.title, description: messages.description, className: className, children: actionLedgerQuery.isPending && entries.length === 0 ? (_jsx(ActionLedgerLoadingRow, {})) : actionLedgerQuery.isError && entries.length === 0 ? (_jsx(ActionLedgerEmptyRow, { children: messages.loadFailed })) : entries.length === 0 ? (_jsx(ActionLedgerEmptyRow, { children: messages.empty })) : (_jsxs("div", { className: "flex flex-col gap-3", children: [_jsx("ul", { className: "divide-y rounded-md border bg-background", children: entries.map((entry) => (_jsx(ProductActionLedgerEntryItem, { entry: entry, timestamp: formatDateTime(entry.occurredAt) }, entry.id))) }), nextCursor ? (_jsxs(Button, { type: "button", variant: "outline", size: "sm", className: "self-start", disabled: actionLedgerQuery.isFetching, onClick: () => setCursor(nextCursor), children: [actionLedgerQuery.isFetching ? (_jsx(Loader2, { className: "size-4 animate-spin", "aria-hidden": "true" })) : null, messages.loadOlder] })) : null] })) }));
|
|
37
|
+
}
|
|
38
|
+
function ProductActionLedgerEntryItem({ entry, timestamp, }) {
|
|
39
|
+
return (_jsxs("li", { className: "flex items-start gap-3 p-3", children: [_jsx(Activity, { className: "mt-0.5 size-4 shrink-0 text-muted-foreground", "aria-hidden": "true" }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx("p", { className: "truncate font-medium text-sm", children: formatActionLedgerName(entry.actionName) }), _jsx(Badge, { variant: actionLedgerStatusVariant[entry.status] ?? "secondary", children: entry.status.replace(/_/g, " ") }), _jsx(Badge, { variant: actionLedgerRiskVariant[entry.evaluatedRisk] ?? "outline", children: entry.evaluatedRisk })] }), _jsxs("p", { className: "mt-1 text-muted-foreground text-xs", children: [entry.principalType, ":", entry.principalId, " - ", timestamp] }), entry.mutationSummary ? (_jsx("p", { className: "mt-1 truncate text-muted-foreground text-xs", children: entry.mutationSummary })) : null] })] }));
|
|
40
|
+
}
|
|
41
|
+
function ActionLedgerSection({ title, description, children, className, }) {
|
|
42
|
+
return (_jsxs("section", { "data-slot": "product-action-ledger-card", className: cn("rounded-md border bg-background", className), children: [_jsxs("div", { className: "space-y-1 border-b px-4 py-3", children: [_jsx("h2", { className: "font-semibold text-sm", children: title }), _jsx("p", { className: "text-muted-foreground text-xs", children: description })] }), _jsx("div", { className: "p-4", children: children })] }));
|
|
43
|
+
}
|
|
44
|
+
function ActionLedgerEmptyRow({ children }) {
|
|
45
|
+
return _jsx("p", { className: "py-6 text-center text-sm text-muted-foreground", children: children });
|
|
46
|
+
}
|
|
47
|
+
function ActionLedgerLoadingRow() {
|
|
48
|
+
return (_jsx("div", { className: "flex justify-center py-6", children: _jsx(Loader2, { className: "size-5 animate-spin text-muted-foreground" }) }));
|
|
49
|
+
}
|
|
50
|
+
function formatActionLedgerName(actionName) {
|
|
51
|
+
const withoutDomain = actionName.replace(/^product\./, "");
|
|
52
|
+
const label = withoutDomain.replace(/[._-]/g, " ");
|
|
53
|
+
return label.charAt(0).toUpperCase() + label.slice(1);
|
|
54
|
+
}
|
|
55
|
+
export const actionLedgerStatusVariant = {
|
|
56
|
+
requested: "outline",
|
|
57
|
+
awaiting_approval: "secondary",
|
|
58
|
+
approved: "secondary",
|
|
59
|
+
denied: "destructive",
|
|
60
|
+
succeeded: "default",
|
|
61
|
+
failed: "destructive",
|
|
62
|
+
reversed: "secondary",
|
|
63
|
+
compensated: "secondary",
|
|
64
|
+
expired: "secondary",
|
|
65
|
+
cancelled: "secondary",
|
|
66
|
+
superseded: "secondary",
|
|
67
|
+
};
|
|
68
|
+
export const actionLedgerRiskVariant = {
|
|
69
|
+
low: "outline",
|
|
70
|
+
medium: "secondary",
|
|
71
|
+
high: "destructive",
|
|
72
|
+
critical: "destructive",
|
|
73
|
+
};
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type ProductRecord } from "@voyantjs/products-react";
|
|
2
2
|
import * as React from "react";
|
|
3
|
-
import type
|
|
4
|
-
import { type ProductItineraryDayRowRenderContext } from "./product-itinerary-day-row.js";
|
|
3
|
+
import { type ProductItinerarySectionProps } from "./product-itinerary-section.js";
|
|
5
4
|
import { type ProductMediaSectionProps } from "./product-media-section.js";
|
|
6
5
|
import { type ProductOptionsSectionProps } from "./product-options-section.js";
|
|
7
6
|
export interface ProductDetailPageSlots {
|
|
@@ -23,10 +22,10 @@ export interface ProductDetailPageProps {
|
|
|
23
22
|
onDeleted?: () => void;
|
|
24
23
|
uploadMedia?: ProductMediaSectionProps["uploadMedia"];
|
|
25
24
|
renderOptionDetails?: ProductOptionsSectionProps["renderOptionDetails"];
|
|
26
|
-
renderItineraryDayDetails?:
|
|
27
|
-
renderItineraryServiceActions?:
|
|
28
|
-
renderSupplierServiceField?:
|
|
29
|
-
onSupplierServiceSelected?:
|
|
25
|
+
renderItineraryDayDetails?: ProductItinerarySectionProps["renderDayDetails"];
|
|
26
|
+
renderItineraryServiceActions?: ProductItinerarySectionProps["renderServiceActions"];
|
|
27
|
+
renderSupplierServiceField?: ProductItinerarySectionProps["renderSupplierServiceField"];
|
|
28
|
+
onSupplierServiceSelected?: ProductItinerarySectionProps["onSupplierServiceSelected"];
|
|
30
29
|
slots?: ProductDetailPageSlots;
|
|
31
30
|
}
|
|
32
31
|
export declare function ProductDetailPage({ id, className, onBack, onBookingCreate, onDeleted, uploadMedia, renderOptionDetails, renderItineraryDayDetails, renderItineraryServiceActions, renderSupplierServiceField, onSupplierServiceSelected, slots, }: ProductDetailPageProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -52,15 +51,4 @@ export interface ProductDetailSidebarProps {
|
|
|
52
51
|
className?: string;
|
|
53
52
|
}
|
|
54
53
|
export declare function ProductDetailSidebar({ product, className }: ProductDetailSidebarProps): import("react/jsx-runtime").JSX.Element;
|
|
55
|
-
export interface ProductItinerarySectionProps {
|
|
56
|
-
productId: string;
|
|
57
|
-
title?: string;
|
|
58
|
-
description?: string;
|
|
59
|
-
renderDayDetails?: (context: ProductItineraryDayRowRenderContext) => React.ReactNode;
|
|
60
|
-
renderServiceActions?: (service: ProductDayServiceRecord) => React.ReactNode;
|
|
61
|
-
renderSupplierServiceField?: ProductDayServiceFormProps["renderSupplierServiceField"];
|
|
62
|
-
onSupplierServiceSelected?: ProductDayServiceFormProps["onSupplierServiceSelected"];
|
|
63
|
-
className?: string;
|
|
64
|
-
}
|
|
65
|
-
export declare function ProductItinerarySection({ productId, title, description, renderDayDetails, renderServiceActions, renderSupplierServiceField, onSupplierServiceSelected, className, }: ProductItinerarySectionProps): import("react/jsx-runtime").JSX.Element;
|
|
66
54
|
//# sourceMappingURL=product-detail-page.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"product-detail-page.d.ts","sourceRoot":"","sources":["../../src/components/product-detail-page.tsx"],"names":[],"mappings":"AAEA,OAAO,
|
|
1
|
+
{"version":3,"file":"product-detail-page.d.ts","sourceRoot":"","sources":["../../src/components/product-detail-page.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1,19 +1,16 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs
|
|
3
|
-
import { useProduct,
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useProduct, useProductMutation } from "@voyantjs/products-react";
|
|
4
4
|
import { Badge } from "@voyantjs/ui/components/badge";
|
|
5
5
|
import { Button } from "@voyantjs/ui/components/button";
|
|
6
6
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@voyantjs/ui/components/card";
|
|
7
|
-
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@voyantjs/ui/components/select";
|
|
8
7
|
import { cn } from "@voyantjs/ui/lib/utils";
|
|
9
|
-
import { ArrowLeft, Edit, FileText, Loader2,
|
|
8
|
+
import { ArrowLeft, Edit, FileText, Loader2, ReceiptText, Trash2 } from "lucide-react";
|
|
10
9
|
import * as React from "react";
|
|
11
10
|
import { useProductsUiI18nOrDefault, useProductsUiMessagesOrDefault } from "../i18n/provider.js";
|
|
12
|
-
import {
|
|
13
|
-
import { ProductDayServiceDialog } from "./product-day-service-dialog.js";
|
|
11
|
+
import { ProductActionLedgerCard } from "./product-action-ledger-card.js";
|
|
14
12
|
import { ProductDialog } from "./product-dialog.js";
|
|
15
|
-
import {
|
|
16
|
-
import { ProductItineraryDialog } from "./product-itinerary-dialog.js";
|
|
13
|
+
import { ProductItinerarySection, } from "./product-itinerary-section.js";
|
|
17
14
|
import { ProductMediaSection } from "./product-media-section.js";
|
|
18
15
|
import { ProductOptionsSection, } from "./product-options-section.js";
|
|
19
16
|
import { ProductVersionsSection } from "./product-versions-section.js";
|
|
@@ -46,7 +43,7 @@ export function ProductDetailPage({ id, className, onBack, onBookingCreate, onDe
|
|
|
46
43
|
setDeleteError(error instanceof Error ? error.message : pageMessages.states.deleteFailed);
|
|
47
44
|
}
|
|
48
45
|
};
|
|
49
|
-
return (_jsxs("div", { "data-slot": "product-detail-page", className: cn("flex flex-col gap-6 p-6", className), children: [_jsx(ProductDetailHeader, { product: product, onBack: onBack, onEdit: () => setEditOpen(true), onDelete: () => void handleDelete(), onBookingCreate: onBookingCreate ? () => onBookingCreate(product) : undefined, deleting: remove.isPending, actionsSlot: slots?.header }), deleteError ? _jsx("p", { className: "text-sm text-destructive", children: deleteError }) : null, slots?.afterHeader, _jsxs("div", { className: "grid gap-6 xl:grid-cols-[minmax(0,1fr)_320px]", children: [_jsxs("div", { className: "flex min-w-0 flex-col gap-6", children: [slots?.overviewStart, _jsx(ProductOverviewCard, { product: product }), _jsx(ProductCommercialCard, { product: product }), slots?.overviewEnd, _jsx(ProductMediaSection, { productId: product.id, uploadMedia: uploadMedia }), slots?.mediaEnd, _jsx(ProductItinerarySection, { productId: product.id, renderDayDetails: renderItineraryDayDetails, renderServiceActions: renderItineraryServiceActions, renderSupplierServiceField: renderSupplierServiceField, onSupplierServiceSelected: onSupplierServiceSelected }), slots?.itineraryEnd, _jsx(ProductOptionsSection, { productId: product.id, renderOptionDetails: renderOptionDetails }), slots?.optionsEnd, _jsx(ProductVersionsSection, { productId: product.id }), slots?.versionsEnd] }), _jsxs("div", { className: "flex min-w-0 flex-col gap-6", children: [_jsx(ProductDetailSidebar, { product: product }), slots?.sidebar] })] }), _jsx(ProductDialog, { open: editOpen, onOpenChange: setEditOpen, product: product, onSuccess: () => setEditOpen(false) })] }));
|
|
46
|
+
return (_jsxs("div", { "data-slot": "product-detail-page", className: cn("flex flex-col gap-6 p-6", className), children: [_jsx(ProductDetailHeader, { product: product, onBack: onBack, onEdit: () => setEditOpen(true), onDelete: () => void handleDelete(), onBookingCreate: onBookingCreate ? () => onBookingCreate(product) : undefined, deleting: remove.isPending, actionsSlot: slots?.header }), deleteError ? _jsx("p", { className: "text-sm text-destructive", children: deleteError }) : null, slots?.afterHeader, _jsxs("div", { className: "grid gap-6 xl:grid-cols-[minmax(0,1fr)_320px]", children: [_jsxs("div", { className: "flex min-w-0 flex-col gap-6", children: [slots?.overviewStart, _jsx(ProductOverviewCard, { product: product }), _jsx(ProductCommercialCard, { product: product }), slots?.overviewEnd, _jsx(ProductMediaSection, { productId: product.id, uploadMedia: uploadMedia }), slots?.mediaEnd, _jsx(ProductItinerarySection, { productId: product.id, renderDayDetails: renderItineraryDayDetails, renderServiceActions: renderItineraryServiceActions, renderSupplierServiceField: renderSupplierServiceField, onSupplierServiceSelected: onSupplierServiceSelected }), slots?.itineraryEnd, _jsx(ProductOptionsSection, { productId: product.id, renderOptionDetails: renderOptionDetails }), slots?.optionsEnd, _jsx(ProductVersionsSection, { productId: product.id }), slots?.versionsEnd] }), _jsxs("div", { className: "flex min-w-0 flex-col gap-6", children: [_jsx(ProductDetailSidebar, { product: product }), _jsx(ProductActionLedgerCard, { productId: product.id }), slots?.sidebar] })] }), _jsx(ProductDialog, { open: editOpen, onOpenChange: setEditOpen, product: product, onSuccess: () => setEditOpen(false) })] }));
|
|
50
47
|
}
|
|
51
48
|
export function ProductDetailHeader({ product, onBack, onEdit, onDelete, onBookingCreate, deleting = false, actionsSlot, className, }) {
|
|
52
49
|
const messages = useProductsUiMessagesOrDefault();
|
|
@@ -75,98 +72,6 @@ export function ProductDetailSidebar({ product, className }) {
|
|
|
75
72
|
const { formatDateTime } = useProductsUiI18nOrDefault();
|
|
76
73
|
return (_jsxs(Card, { "data-slot": "product-detail-sidebar", className: className, children: [_jsxs(CardHeader, { children: [_jsx(CardTitle, { children: pageMessages.sections.sidebar.title }), _jsx(CardDescription, { children: pageMessages.sections.sidebar.description })] }), _jsxs(CardContent, { className: "space-y-4", children: [_jsxs("div", { className: "flex flex-wrap gap-2", children: [_jsx(Badge, { children: messages.common.productStatusLabels[product.status] }), _jsx(Badge, { variant: "outline", children: messages.common.productBookingModeLabels[product.bookingMode] }), product.activated ? _jsx(Badge, { variant: "secondary", children: messages.common.active }) : null] }), _jsx(ProductField, { label: pageMessages.fields.tags, value: product.tags.join(", ") }), _jsx(ProductField, { label: pageMessages.fields.createdAt, value: formatDateTime(product.createdAt) }), _jsx(ProductField, { label: pageMessages.fields.updatedAt, value: formatDateTime(product.updatedAt) })] })] }));
|
|
77
74
|
}
|
|
78
|
-
export function ProductItinerarySection({ productId, title, description, renderDayDetails, renderServiceActions, renderSupplierServiceField, onSupplierServiceSelected, className, }) {
|
|
79
|
-
const messages = useProductsUiMessagesOrDefault();
|
|
80
|
-
const pageMessages = messages.productDetailPage;
|
|
81
|
-
const itinerariesQuery = useProductItineraries(productId);
|
|
82
|
-
const itineraries = React.useMemo(() => (itinerariesQuery.data?.data ?? []).slice().sort((a, b) => a.sortOrder - b.sortOrder), [itinerariesQuery.data?.data]);
|
|
83
|
-
const [selectedItineraryId, setSelectedItineraryId] = React.useState(null);
|
|
84
|
-
const selectedItinerary = itineraries.find((itinerary) => itinerary.id === selectedItineraryId) ?? null;
|
|
85
|
-
const daysQuery = useProductItineraryDays(productId, selectedItineraryId, {
|
|
86
|
-
enabled: Boolean(selectedItineraryId),
|
|
87
|
-
});
|
|
88
|
-
const days = React.useMemo(() => (daysQuery.data?.data ?? []).slice().sort((a, b) => a.dayNumber - b.dayNumber), [daysQuery.data?.data]);
|
|
89
|
-
const itineraryMutation = useProductItineraryMutation();
|
|
90
|
-
const dayMutation = useProductDayMutation();
|
|
91
|
-
const serviceMutation = useProductDayServiceMutation();
|
|
92
|
-
const [itineraryDialogOpen, setItineraryDialogOpen] = React.useState(false);
|
|
93
|
-
const [editingItinerary, setEditingItinerary] = React.useState(null);
|
|
94
|
-
const [dayDialogOpen, setDayDialogOpen] = React.useState(false);
|
|
95
|
-
const [editingDay, setEditingDay] = React.useState(null);
|
|
96
|
-
const [serviceDialogOpen, setServiceDialogOpen] = React.useState(false);
|
|
97
|
-
const [serviceDayId, setServiceDayId] = React.useState(null);
|
|
98
|
-
const [editingService, setEditingService] = React.useState(null);
|
|
99
|
-
const [expandedDayId, setExpandedDayId] = React.useState(null);
|
|
100
|
-
React.useEffect(() => {
|
|
101
|
-
if (itineraries.length === 0) {
|
|
102
|
-
setSelectedItineraryId(null);
|
|
103
|
-
return;
|
|
104
|
-
}
|
|
105
|
-
setSelectedItineraryId((current) => {
|
|
106
|
-
if (current && itineraries.some((itinerary) => itinerary.id === current))
|
|
107
|
-
return current;
|
|
108
|
-
return itineraries.find((itinerary) => itinerary.isDefault)?.id ?? itineraries[0]?.id ?? null;
|
|
109
|
-
});
|
|
110
|
-
}, [itineraries]);
|
|
111
|
-
const nextDayNumber = days.length > 0 ? Math.max(...days.map((day) => day.dayNumber)) + 1 : 1;
|
|
112
|
-
return (_jsxs(Card, { "data-slot": "product-itinerary-section", className: className, children: [_jsxs(CardHeader, { className: "flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between", children: [_jsxs("div", { className: "space-y-1", children: [_jsx(CardTitle, { children: title ?? pageMessages.sections.itinerary.title }), _jsx(CardDescription, { children: description ?? pageMessages.sections.itinerary.description })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [itineraries.length > 0 ? (_jsxs(Select, { value: selectedItineraryId ?? undefined, onValueChange: (value) => setSelectedItineraryId(value), children: [_jsx(SelectTrigger, { className: "w-[220px]", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: itineraries.map((itinerary) => (_jsx(SelectItem, { value: itinerary.id, children: itinerary.name }, itinerary.id))) })] })) : null, _jsxs(Button, { type: "button", variant: "outline", onClick: () => {
|
|
113
|
-
setEditingItinerary(null);
|
|
114
|
-
setItineraryDialogOpen(true);
|
|
115
|
-
}, children: [_jsx(Plus, { className: "mr-2 size-4", "aria-hidden": "true" }), pageMessages.actions.addItinerary] }), selectedItinerary ? (_jsxs(_Fragment, { children: [_jsxs(Button, { type: "button", variant: "outline", onClick: () => {
|
|
116
|
-
setEditingItinerary(selectedItinerary);
|
|
117
|
-
setItineraryDialogOpen(true);
|
|
118
|
-
}, children: [_jsx(Edit, { className: "mr-2 size-4", "aria-hidden": "true" }), pageMessages.actions.editItinerary] }), _jsxs(Button, { type: "button", variant: "outline", onClick: () => {
|
|
119
|
-
if (!confirm(pageMessages.states.deleteItineraryConfirm.replace("{name}", selectedItinerary.name))) {
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
void itineraryMutation.remove.mutateAsync({
|
|
123
|
-
productId,
|
|
124
|
-
itineraryId: selectedItinerary.id,
|
|
125
|
-
});
|
|
126
|
-
}, children: [_jsx(Trash2, { className: "mr-2 size-4", "aria-hidden": "true" }), pageMessages.actions.deleteItinerary] })] })) : null] })] }), _jsx(CardContent, { className: "flex flex-col gap-3", children: itinerariesQuery.isPending ? (_jsx("div", { className: "flex min-h-24 items-center justify-center", children: _jsx(Loader2, { className: "size-4 animate-spin text-muted-foreground", "aria-hidden": "true" }) })) : itinerariesQuery.isError ? (_jsx("p", { className: "text-sm text-destructive", children: pageMessages.states.loadFailed })) : itineraries.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: pageMessages.states.noItineraries })) : (_jsxs(_Fragment, { children: [_jsx("div", { className: "flex justify-end", children: _jsxs(Button, { type: "button", onClick: () => {
|
|
127
|
-
setEditingDay(null);
|
|
128
|
-
setDayDialogOpen(true);
|
|
129
|
-
}, disabled: !selectedItineraryId, children: [_jsx(Plus, { className: "mr-2 size-4", "aria-hidden": "true" }), pageMessages.actions.addDay] }) }), daysQuery.isPending ? (_jsx("div", { className: "flex min-h-24 items-center justify-center", children: _jsx(Loader2, { className: "size-4 animate-spin text-muted-foreground", "aria-hidden": "true" }) })) : daysQuery.isError ? (_jsx("p", { className: "text-sm text-destructive", children: pageMessages.states.loadFailed })) : days.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: pageMessages.states.noDays })) : (days.map((day) => (_jsx(ProductItineraryDayRow, { productId: productId, day: day, expanded: expandedDayId === day.id, onToggle: () => setExpandedDayId((current) => (current === day.id ? null : day.id)), onEdit: () => {
|
|
130
|
-
setEditingDay(day);
|
|
131
|
-
setDayDialogOpen(true);
|
|
132
|
-
}, onDelete: () => {
|
|
133
|
-
if (!confirm(pageMessages.states.deleteDayConfirm.replace("{dayNumber}", String(day.dayNumber)))) {
|
|
134
|
-
return;
|
|
135
|
-
}
|
|
136
|
-
void dayMutation.remove.mutateAsync({
|
|
137
|
-
productId,
|
|
138
|
-
dayId: day.id,
|
|
139
|
-
itineraryId: day.itineraryId,
|
|
140
|
-
});
|
|
141
|
-
}, onAddService: () => {
|
|
142
|
-
setServiceDayId(day.id);
|
|
143
|
-
setEditingService(null);
|
|
144
|
-
setExpandedDayId(day.id);
|
|
145
|
-
setServiceDialogOpen(true);
|
|
146
|
-
}, onEditService: (service) => {
|
|
147
|
-
setServiceDayId(day.id);
|
|
148
|
-
setEditingService(service);
|
|
149
|
-
setExpandedDayId(day.id);
|
|
150
|
-
setServiceDialogOpen(true);
|
|
151
|
-
}, onDeleteService: (service) => {
|
|
152
|
-
if (!confirm(pageMessages.states.deleteServiceConfirm.replace("{name}", service.name))) {
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
155
|
-
void serviceMutation.remove.mutateAsync({
|
|
156
|
-
productId,
|
|
157
|
-
dayId: day.id,
|
|
158
|
-
serviceId: service.id,
|
|
159
|
-
});
|
|
160
|
-
}, renderDayDetails: renderDayDetails, renderServiceActions: renderServiceActions }, day.id))))] })) }), _jsx(ProductItineraryDialog, { open: itineraryDialogOpen, onOpenChange: setItineraryDialogOpen, productId: productId, itinerary: editingItinerary ?? undefined, itineraryCount: itineraries.length, onSuccess: (itineraryId) => setSelectedItineraryId(itineraryId) }), selectedItineraryId ? (_jsx(ProductDayDialog, { open: dayDialogOpen, onOpenChange: setDayDialogOpen, productId: productId, itineraryId: selectedItineraryId, day: editingDay ?? undefined, nextDayNumber: nextDayNumber, onSuccess: (day) => {
|
|
161
|
-
setExpandedDayId(day.id);
|
|
162
|
-
setEditingDay(null);
|
|
163
|
-
} })) : null, serviceDayId ? (_jsx(ProductDayServiceDialog, { open: serviceDialogOpen, onOpenChange: (open) => {
|
|
164
|
-
setServiceDialogOpen(open);
|
|
165
|
-
if (!open) {
|
|
166
|
-
setEditingService(null);
|
|
167
|
-
}
|
|
168
|
-
}, productId: productId, dayId: serviceDayId, service: editingService ?? undefined, renderSupplierServiceField: renderSupplierServiceField, onSupplierServiceSelected: onSupplierServiceSelected, onSuccess: () => setEditingService(null) })) : null] }));
|
|
169
|
-
}
|
|
170
75
|
function ProductDetailPageLoading({ className }) {
|
|
171
76
|
const messages = useProductsUiMessagesOrDefault();
|
|
172
77
|
return (_jsx("div", { "data-slot": "product-detail-page-loading", className: cn("flex min-h-48 items-center justify-center p-6", className), children: _jsxs("div", { className: "flex items-center gap-2 text-sm text-muted-foreground", children: [_jsx(Loader2, { className: "size-4 animate-spin", "aria-hidden": "true" }), messages.productDetailPage.states.loading] }) }));
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type ProductDayServiceRecord } from "@voyantjs/products-react";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import type { ProductDayServiceFormProps } from "./product-day-service-form.js";
|
|
4
|
+
import { type ProductItineraryDayRowRenderContext } from "./product-itinerary-day-row.js";
|
|
5
|
+
export interface ProductItinerarySectionProps {
|
|
6
|
+
productId: string;
|
|
7
|
+
title?: string;
|
|
8
|
+
description?: string;
|
|
9
|
+
renderDayDetails?: (context: ProductItineraryDayRowRenderContext) => React.ReactNode;
|
|
10
|
+
renderServiceActions?: (service: ProductDayServiceRecord) => React.ReactNode;
|
|
11
|
+
renderSupplierServiceField?: ProductDayServiceFormProps["renderSupplierServiceField"];
|
|
12
|
+
onSupplierServiceSelected?: ProductDayServiceFormProps["onSupplierServiceSelected"];
|
|
13
|
+
className?: string;
|
|
14
|
+
}
|
|
15
|
+
export declare function ProductItinerarySection({ productId, title, description, renderDayDetails, renderServiceActions, renderSupplierServiceField, onSupplierServiceSelected, className, }: ProductItinerarySectionProps): import("react/jsx-runtime").JSX.Element;
|
|
16
|
+
//# sourceMappingURL=product-itinerary-section.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"product-itinerary-section.d.ts","sourceRoot":"","sources":["../../src/components/product-itinerary-section.tsx"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,uBAAuB,EAO7B,MAAM,0BAA0B,CAAA;AAiBjC,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAK9B,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,+BAA+B,CAAA;AAC/E,OAAO,EAEL,KAAK,mCAAmC,EACzC,MAAM,gCAAgC,CAAA;AAGvC,MAAM,WAAW,4BAA4B;IAC3C,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,gBAAgB,CAAC,EAAE,CAAC,OAAO,EAAE,mCAAmC,KAAK,KAAK,CAAC,SAAS,CAAA;IACpF,oBAAoB,CAAC,EAAE,CAAC,OAAO,EAAE,uBAAuB,KAAK,KAAK,CAAC,SAAS,CAAA;IAC5E,0BAA0B,CAAC,EAAE,0BAA0B,CAAC,4BAA4B,CAAC,CAAA;IACrF,yBAAyB,CAAC,EAAE,0BAA0B,CAAC,2BAA2B,CAAC,CAAA;IACnF,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,uBAAuB,CAAC,EACtC,SAAS,EACT,KAAK,EACL,WAAW,EACX,gBAAgB,EAChB,oBAAoB,EACpB,0BAA0B,EAC1B,yBAAyB,EACzB,SAAS,GACV,EAAE,4BAA4B,2CAuQ9B"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { useProductDayMutation, useProductDayServiceMutation, useProductItineraries, useProductItineraryDays, useProductItineraryMutation, } from "@voyantjs/products-react";
|
|
4
|
+
import { Button } from "@voyantjs/ui/components/button";
|
|
5
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@voyantjs/ui/components/card";
|
|
6
|
+
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@voyantjs/ui/components/select";
|
|
7
|
+
import { Edit, Loader2, Plus, Trash2 } from "lucide-react";
|
|
8
|
+
import * as React from "react";
|
|
9
|
+
import { useProductsUiMessagesOrDefault } from "../i18n/provider.js";
|
|
10
|
+
import { ProductDayDialog } from "./product-day-dialog.js";
|
|
11
|
+
import { ProductDayServiceDialog } from "./product-day-service-dialog.js";
|
|
12
|
+
import { ProductItineraryDayRow, } from "./product-itinerary-day-row.js";
|
|
13
|
+
import { ProductItineraryDialog } from "./product-itinerary-dialog.js";
|
|
14
|
+
export function ProductItinerarySection({ productId, title, description, renderDayDetails, renderServiceActions, renderSupplierServiceField, onSupplierServiceSelected, className, }) {
|
|
15
|
+
const messages = useProductsUiMessagesOrDefault();
|
|
16
|
+
const pageMessages = messages.productDetailPage;
|
|
17
|
+
const itinerariesQuery = useProductItineraries(productId);
|
|
18
|
+
const itineraries = React.useMemo(() => (itinerariesQuery.data?.data ?? []).slice().sort((a, b) => a.sortOrder - b.sortOrder), [itinerariesQuery.data?.data]);
|
|
19
|
+
const [selectedItineraryId, setSelectedItineraryId] = React.useState(null);
|
|
20
|
+
const selectedItinerary = itineraries.find((itinerary) => itinerary.id === selectedItineraryId) ?? null;
|
|
21
|
+
const daysQuery = useProductItineraryDays(productId, selectedItineraryId, {
|
|
22
|
+
enabled: Boolean(selectedItineraryId),
|
|
23
|
+
});
|
|
24
|
+
const days = React.useMemo(() => (daysQuery.data?.data ?? []).slice().sort((a, b) => a.dayNumber - b.dayNumber), [daysQuery.data?.data]);
|
|
25
|
+
const itineraryMutation = useProductItineraryMutation();
|
|
26
|
+
const dayMutation = useProductDayMutation();
|
|
27
|
+
const serviceMutation = useProductDayServiceMutation();
|
|
28
|
+
const [itineraryDialogOpen, setItineraryDialogOpen] = React.useState(false);
|
|
29
|
+
const [editingItinerary, setEditingItinerary] = React.useState(null);
|
|
30
|
+
const [dayDialogOpen, setDayDialogOpen] = React.useState(false);
|
|
31
|
+
const [editingDay, setEditingDay] = React.useState(null);
|
|
32
|
+
const [serviceDialogOpen, setServiceDialogOpen] = React.useState(false);
|
|
33
|
+
const [serviceDayId, setServiceDayId] = React.useState(null);
|
|
34
|
+
const [editingService, setEditingService] = React.useState(null);
|
|
35
|
+
const [expandedDayId, setExpandedDayId] = React.useState(null);
|
|
36
|
+
React.useEffect(() => {
|
|
37
|
+
if (itineraries.length === 0) {
|
|
38
|
+
setSelectedItineraryId(null);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
setSelectedItineraryId((current) => {
|
|
42
|
+
if (current && itineraries.some((itinerary) => itinerary.id === current))
|
|
43
|
+
return current;
|
|
44
|
+
return itineraries.find((itinerary) => itinerary.isDefault)?.id ?? itineraries[0]?.id ?? null;
|
|
45
|
+
});
|
|
46
|
+
}, [itineraries]);
|
|
47
|
+
const nextDayNumber = days.length > 0 ? Math.max(...days.map((day) => day.dayNumber)) + 1 : 1;
|
|
48
|
+
return (_jsxs(Card, { "data-slot": "product-itinerary-section", className: className, children: [_jsxs(CardHeader, { className: "flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between", children: [_jsxs("div", { className: "space-y-1", children: [_jsx(CardTitle, { children: title ?? pageMessages.sections.itinerary.title }), _jsx(CardDescription, { children: description ?? pageMessages.sections.itinerary.description })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [itineraries.length > 0 ? (_jsxs(Select, { value: selectedItineraryId ?? undefined, onValueChange: (value) => setSelectedItineraryId(value), children: [_jsx(SelectTrigger, { className: "w-[220px]", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: itineraries.map((itinerary) => (_jsx(SelectItem, { value: itinerary.id, children: itinerary.name }, itinerary.id))) })] })) : null, _jsxs(Button, { type: "button", variant: "outline", onClick: () => {
|
|
49
|
+
setEditingItinerary(null);
|
|
50
|
+
setItineraryDialogOpen(true);
|
|
51
|
+
}, children: [_jsx(Plus, { className: "mr-2 size-4", "aria-hidden": "true" }), pageMessages.actions.addItinerary] }), selectedItinerary ? (_jsxs(_Fragment, { children: [_jsxs(Button, { type: "button", variant: "outline", onClick: () => {
|
|
52
|
+
setEditingItinerary(selectedItinerary);
|
|
53
|
+
setItineraryDialogOpen(true);
|
|
54
|
+
}, children: [_jsx(Edit, { className: "mr-2 size-4", "aria-hidden": "true" }), pageMessages.actions.editItinerary] }), _jsxs(Button, { type: "button", variant: "outline", onClick: () => {
|
|
55
|
+
if (!confirm(pageMessages.states.deleteItineraryConfirm.replace("{name}", selectedItinerary.name))) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
void itineraryMutation.remove.mutateAsync({
|
|
59
|
+
productId,
|
|
60
|
+
itineraryId: selectedItinerary.id,
|
|
61
|
+
});
|
|
62
|
+
}, children: [_jsx(Trash2, { className: "mr-2 size-4", "aria-hidden": "true" }), pageMessages.actions.deleteItinerary] })] })) : null] })] }), _jsx(CardContent, { className: "flex flex-col gap-3", children: itinerariesQuery.isPending ? (_jsx("div", { className: "flex min-h-24 items-center justify-center", children: _jsx(Loader2, { className: "size-4 animate-spin text-muted-foreground", "aria-hidden": "true" }) })) : itinerariesQuery.isError ? (_jsx("p", { className: "text-sm text-destructive", children: pageMessages.states.loadFailed })) : itineraries.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: pageMessages.states.noItineraries })) : (_jsxs(_Fragment, { children: [_jsx("div", { className: "flex justify-end", children: _jsxs(Button, { type: "button", onClick: () => {
|
|
63
|
+
setEditingDay(null);
|
|
64
|
+
setDayDialogOpen(true);
|
|
65
|
+
}, disabled: !selectedItineraryId, children: [_jsx(Plus, { className: "mr-2 size-4", "aria-hidden": "true" }), pageMessages.actions.addDay] }) }), daysQuery.isPending ? (_jsx("div", { className: "flex min-h-24 items-center justify-center", children: _jsx(Loader2, { className: "size-4 animate-spin text-muted-foreground", "aria-hidden": "true" }) })) : daysQuery.isError ? (_jsx("p", { className: "text-sm text-destructive", children: pageMessages.states.loadFailed })) : days.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: pageMessages.states.noDays })) : (days.map((day) => (_jsx(ProductItineraryDayRow, { productId: productId, day: day, expanded: expandedDayId === day.id, onToggle: () => setExpandedDayId((current) => (current === day.id ? null : day.id)), onEdit: () => {
|
|
66
|
+
setEditingDay(day);
|
|
67
|
+
setDayDialogOpen(true);
|
|
68
|
+
}, onDelete: () => {
|
|
69
|
+
if (!confirm(pageMessages.states.deleteDayConfirm.replace("{dayNumber}", String(day.dayNumber)))) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
void dayMutation.remove.mutateAsync({
|
|
73
|
+
productId,
|
|
74
|
+
dayId: day.id,
|
|
75
|
+
itineraryId: day.itineraryId,
|
|
76
|
+
});
|
|
77
|
+
}, onAddService: () => {
|
|
78
|
+
setServiceDayId(day.id);
|
|
79
|
+
setEditingService(null);
|
|
80
|
+
setExpandedDayId(day.id);
|
|
81
|
+
setServiceDialogOpen(true);
|
|
82
|
+
}, onEditService: (service) => {
|
|
83
|
+
setServiceDayId(day.id);
|
|
84
|
+
setEditingService(service);
|
|
85
|
+
setExpandedDayId(day.id);
|
|
86
|
+
setServiceDialogOpen(true);
|
|
87
|
+
}, onDeleteService: (service) => {
|
|
88
|
+
if (!confirm(pageMessages.states.deleteServiceConfirm.replace("{name}", service.name))) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
void serviceMutation.remove.mutateAsync({
|
|
92
|
+
productId,
|
|
93
|
+
dayId: day.id,
|
|
94
|
+
serviceId: service.id,
|
|
95
|
+
});
|
|
96
|
+
}, renderDayDetails: renderDayDetails, renderServiceActions: renderServiceActions }, day.id))))] })) }), _jsx(ProductItineraryDialog, { open: itineraryDialogOpen, onOpenChange: setItineraryDialogOpen, productId: productId, itinerary: editingItinerary ?? undefined, itineraryCount: itineraries.length, onSuccess: (itineraryId) => setSelectedItineraryId(itineraryId) }), selectedItineraryId ? (_jsx(ProductDayDialog, { open: dayDialogOpen, onOpenChange: setDayDialogOpen, productId: productId, itineraryId: selectedItineraryId, day: editingDay ?? undefined, nextDayNumber: nextDayNumber, onSuccess: (day) => {
|
|
97
|
+
setExpandedDayId(day.id);
|
|
98
|
+
setEditingDay(null);
|
|
99
|
+
} })) : null, serviceDayId ? (_jsx(ProductDayServiceDialog, { open: serviceDialogOpen, onOpenChange: (open) => {
|
|
100
|
+
setServiceDialogOpen(open);
|
|
101
|
+
if (!open) {
|
|
102
|
+
setEditingService(null);
|
|
103
|
+
}
|
|
104
|
+
}, productId: productId, dayId: serviceDayId, service: editingService ?? undefined, renderSupplierServiceField: renderSupplierServiceField, onSupplierServiceSelected: onSupplierServiceSelected, onSuccess: () => setEditingService(null) })) : null] }));
|
|
105
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { OptionUnitDialog, type OptionUnitDialogProps } from "./components/option-unit-dialog.js";
|
|
2
2
|
export { OptionUnitForm, type OptionUnitFormProps } from "./components/option-unit-form.js";
|
|
3
|
+
export { ProductActionLedgerCard, type ProductActionLedgerCardMessages, type ProductActionLedgerCardProps, } from "./components/product-action-ledger-card.js";
|
|
3
4
|
export { ProductCategoriesPage, type ProductCategoriesPageProps, } from "./components/product-categories-page.js";
|
|
4
5
|
export { ProductCategoryCombobox } from "./components/product-category-combobox.js";
|
|
5
6
|
export { ProductCategoryDialog, type ProductCategoryDialogProps, } from "./components/product-category-dialog.js";
|
|
@@ -11,12 +12,13 @@ export { ProductDayForm, type ProductDayFormProps } from "./components/product-d
|
|
|
11
12
|
export { ProductDayMediaTray, type ProductDayMediaTrayProps, } from "./components/product-day-media-tray.js";
|
|
12
13
|
export { ProductDayServiceDialog, type ProductDayServiceDialogProps, } from "./components/product-day-service-dialog.js";
|
|
13
14
|
export { ProductDayServiceForm, type ProductDayServiceFormHelpers, type ProductDayServiceFormProps, type ProductDayServiceSupplierServiceFieldProps, type ProductDayServiceSupplierServiceOption, } from "./components/product-day-service-form.js";
|
|
14
|
-
export { ProductCommercialCard, ProductDetailHeader, type ProductDetailHeaderProps, ProductDetailPage, type ProductDetailPageProps, type ProductDetailPageSlots, ProductDetailSidebar, type ProductDetailSidebarProps,
|
|
15
|
+
export { ProductCommercialCard, ProductDetailHeader, type ProductDetailHeaderProps, ProductDetailPage, type ProductDetailPageProps, type ProductDetailPageSlots, ProductDetailSidebar, type ProductDetailSidebarProps, ProductOverviewCard, type ProductOverviewCardProps, } from "./components/product-detail-page.js";
|
|
15
16
|
export { ProductDialog, type ProductDialogProps } from "./components/product-dialog.js";
|
|
16
17
|
export { ProductFacilityCombobox } from "./components/product-facility-combobox.js";
|
|
17
18
|
export { ProductForm, type ProductFormMode, type ProductFormProps, } from "./components/product-form.js";
|
|
18
19
|
export { ProductItineraryDayRow, type ProductItineraryDayRowProps, type ProductItineraryDayRowRenderContext, } from "./components/product-itinerary-day-row.js";
|
|
19
20
|
export { ProductItineraryDialog, type ProductItineraryDialogProps, } from "./components/product-itinerary-dialog.js";
|
|
21
|
+
export { ProductItinerarySection, type ProductItinerarySectionProps, } from "./components/product-itinerary-section.js";
|
|
20
22
|
export { ProductList, type ProductListProps } from "./components/product-list.js";
|
|
21
23
|
export { ProductMediaDialog, type ProductMediaDialogProps, } from "./components/product-media-dialog.js";
|
|
22
24
|
export { ProductMediaForm, type ProductMediaFormProps } from "./components/product-media-form.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,KAAK,qBAAqB,EAAE,MAAM,oCAAoC,CAAA;AACjG,OAAO,EAAE,cAAc,EAAE,KAAK,mBAAmB,EAAE,MAAM,kCAAkC,CAAA;AAC3F,OAAO,EACL,qBAAqB,EACrB,KAAK,0BAA0B,GAChC,MAAM,yCAAyC,CAAA;AAChD,OAAO,EAAE,uBAAuB,EAAE,MAAM,2CAA2C,CAAA;AACnF,OAAO,EACL,qBAAqB,EACrB,KAAK,0BAA0B,GAChC,MAAM,yCAAyC,CAAA;AAChD,OAAO,EACL,mBAAmB,EACnB,KAAK,wBAAwB,GAC9B,MAAM,uCAAuC,CAAA;AAC9C,OAAO,EACL,mBAAmB,EACnB,KAAK,wBAAwB,GAC9B,MAAM,uCAAuC,CAAA;AAC9C,OAAO,EAAE,eAAe,EAAE,KAAK,oBAAoB,EAAE,MAAM,kCAAkC,CAAA;AAC7F,OAAO,EAAE,gBAAgB,EAAE,KAAK,qBAAqB,EAAE,MAAM,oCAAoC,CAAA;AACjG,OAAO,EAAE,cAAc,EAAE,KAAK,mBAAmB,EAAE,MAAM,kCAAkC,CAAA;AAC3F,OAAO,EACL,mBAAmB,EACnB,KAAK,wBAAwB,GAC9B,MAAM,wCAAwC,CAAA;AAC/C,OAAO,EACL,uBAAuB,EACvB,KAAK,4BAA4B,GAClC,MAAM,4CAA4C,CAAA;AACnD,OAAO,EACL,qBAAqB,EACrB,KAAK,4BAA4B,EACjC,KAAK,0BAA0B,EAC/B,KAAK,0CAA0C,EAC/C,KAAK,sCAAsC,GAC5C,MAAM,0CAA0C,CAAA;AACjD,OAAO,EACL,qBAAqB,EACrB,mBAAmB,EACnB,KAAK,wBAAwB,EAC7B,iBAAiB,EACjB,KAAK,sBAAsB,EAC3B,KAAK,sBAAsB,EAC3B,oBAAoB,EACpB,KAAK,yBAAyB,EAC9B,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { OptionUnitDialog } from "./components/option-unit-dialog.js";
|
|
2
2
|
export { OptionUnitForm } from "./components/option-unit-form.js";
|
|
3
|
+
export { ProductActionLedgerCard, } from "./components/product-action-ledger-card.js";
|
|
3
4
|
export { ProductCategoriesPage, } from "./components/product-categories-page.js";
|
|
4
5
|
export { ProductCategoryCombobox } from "./components/product-category-combobox.js";
|
|
5
6
|
export { ProductCategoryDialog, } from "./components/product-category-dialog.js";
|
|
@@ -11,12 +12,13 @@ export { ProductDayForm } from "./components/product-day-form.js";
|
|
|
11
12
|
export { ProductDayMediaTray, } from "./components/product-day-media-tray.js";
|
|
12
13
|
export { ProductDayServiceDialog, } from "./components/product-day-service-dialog.js";
|
|
13
14
|
export { ProductDayServiceForm, } from "./components/product-day-service-form.js";
|
|
14
|
-
export { ProductCommercialCard, ProductDetailHeader, ProductDetailPage, ProductDetailSidebar,
|
|
15
|
+
export { ProductCommercialCard, ProductDetailHeader, ProductDetailPage, ProductDetailSidebar, ProductOverviewCard, } from "./components/product-detail-page.js";
|
|
15
16
|
export { ProductDialog } from "./components/product-dialog.js";
|
|
16
17
|
export { ProductFacilityCombobox } from "./components/product-facility-combobox.js";
|
|
17
18
|
export { ProductForm, } from "./components/product-form.js";
|
|
18
19
|
export { ProductItineraryDayRow, } from "./components/product-itinerary-day-row.js";
|
|
19
20
|
export { ProductItineraryDialog, } from "./components/product-itinerary-dialog.js";
|
|
21
|
+
export { ProductItinerarySection, } from "./components/product-itinerary-section.js";
|
|
20
22
|
export { ProductList } from "./components/product-list.js";
|
|
21
23
|
export { ProductMediaDialog, } from "./components/product-media-dialog.js";
|
|
22
24
|
export { ProductMediaForm } from "./components/product-media-form.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@voyantjs/products-ui",
|
|
3
|
-
"version": "0.52.
|
|
3
|
+
"version": "0.52.3",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -45,17 +45,17 @@
|
|
|
45
45
|
"react-dom": "^19.0.0",
|
|
46
46
|
"react-hook-form": "^7.60.0",
|
|
47
47
|
"zod": "^4.3.6",
|
|
48
|
-
"@voyantjs/availability-react": "0.52.
|
|
49
|
-
"@voyantjs/catalog-react": "0.52.
|
|
50
|
-
"@voyantjs/finance": "0.52.
|
|
51
|
-
"@voyantjs/finance-ui": "0.52.
|
|
52
|
-
"@voyantjs/pricing-react": "0.52.
|
|
53
|
-
"@voyantjs/products-react": "0.52.
|
|
54
|
-
"@voyantjs/suppliers-react": "0.52.
|
|
55
|
-
"@voyantjs/ui": "0.52.
|
|
48
|
+
"@voyantjs/availability-react": "0.52.3",
|
|
49
|
+
"@voyantjs/catalog-react": "0.52.3",
|
|
50
|
+
"@voyantjs/finance": "0.52.3",
|
|
51
|
+
"@voyantjs/finance-ui": "0.52.3",
|
|
52
|
+
"@voyantjs/pricing-react": "0.52.3",
|
|
53
|
+
"@voyantjs/products-react": "0.52.3",
|
|
54
|
+
"@voyantjs/suppliers-react": "0.52.3",
|
|
55
|
+
"@voyantjs/ui": "0.52.3"
|
|
56
56
|
},
|
|
57
57
|
"dependencies": {
|
|
58
|
-
"@voyantjs/i18n": "0.52.
|
|
58
|
+
"@voyantjs/i18n": "0.52.3"
|
|
59
59
|
},
|
|
60
60
|
"devDependencies": {
|
|
61
61
|
"@tanstack/react-query": "^5.96.2",
|
|
@@ -68,16 +68,16 @@
|
|
|
68
68
|
"typescript": "^6.0.2",
|
|
69
69
|
"vitest": "^4.1.2",
|
|
70
70
|
"zod": "^4.3.6",
|
|
71
|
-
"@voyantjs/availability-react": "0.52.
|
|
72
|
-
"@voyantjs/catalog-react": "0.52.
|
|
73
|
-
"@voyantjs/finance": "0.52.
|
|
74
|
-
"@voyantjs/finance-ui": "0.52.
|
|
75
|
-
"@voyantjs/i18n": "0.52.
|
|
76
|
-
"@voyantjs/pricing-react": "0.52.
|
|
77
|
-
"@voyantjs/products-react": "0.52.
|
|
78
|
-
"@voyantjs/suppliers-react": "0.52.
|
|
71
|
+
"@voyantjs/availability-react": "0.52.3",
|
|
72
|
+
"@voyantjs/catalog-react": "0.52.3",
|
|
73
|
+
"@voyantjs/finance": "0.52.3",
|
|
74
|
+
"@voyantjs/finance-ui": "0.52.3",
|
|
75
|
+
"@voyantjs/i18n": "0.52.3",
|
|
76
|
+
"@voyantjs/pricing-react": "0.52.3",
|
|
77
|
+
"@voyantjs/products-react": "0.52.3",
|
|
78
|
+
"@voyantjs/suppliers-react": "0.52.3",
|
|
79
79
|
"@voyantjs/voyant-typescript-config": "0.1.0",
|
|
80
|
-
"@voyantjs/ui": "0.52.
|
|
80
|
+
"@voyantjs/ui": "0.52.3"
|
|
81
81
|
},
|
|
82
82
|
"files": [
|
|
83
83
|
"dist",
|