@voyantjs/cruises-ui 0.16.0 → 0.17.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/README.md +14 -0
- package/dist/components/enrichment-program-list.d.ts.map +1 -1
- package/dist/components/enrichment-program-list.js +6 -11
- package/dist/components/external-badge.d.ts.map +1 -1
- package/dist/components/external-badge.js +5 -1
- package/dist/components/pricing-grid.d.ts.map +1 -1
- package/dist/components/pricing-grid.js +36 -25
- package/dist/components/quote-display.d.ts.map +1 -1
- package/dist/components/quote-display.js +42 -23
- package/dist/i18n/en.d.ts +3 -0
- package/dist/i18n/en.d.ts.map +1 -0
- package/dist/i18n/en.js +73 -0
- package/dist/i18n/index.d.ts +5 -0
- package/dist/i18n/index.d.ts.map +1 -0
- package/dist/i18n/index.js +3 -0
- package/dist/i18n/messages.d.ts +56 -0
- package/dist/i18n/messages.d.ts.map +1 -0
- package/dist/i18n/messages.js +1 -0
- package/dist/i18n/provider.d.ts +26 -0
- package/dist/i18n/provider.d.ts.map +1 -0
- package/dist/i18n/provider.js +44 -0
- package/dist/i18n/ro.d.ts +3 -0
- package/dist/i18n/ro.d.ts.map +1 -0
- package/dist/i18n/ro.js +73 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/package.json +21 -5
package/README.md
CHANGED
|
@@ -11,3 +11,17 @@ pnpm add @voyantjs/cruises-ui @voyantjs/cruises-react @voyantjs/ui @tanstack/rea
|
|
|
11
11
|
`@voyantjs/ui` provides the design-system primitives. `@voyantjs/cruises-react` provides the data-layer hooks. Both are required peers.
|
|
12
12
|
|
|
13
13
|
All components accept a `className` prop and merge it with `cn()`. Wrap or compose to extend; use the registry copy-paste path (`npx shadcn add @voyant/...`) for components you want to fork outright.
|
|
14
|
+
|
|
15
|
+
## I18n
|
|
16
|
+
|
|
17
|
+
Components render English by default. To localize them, wrap your UI in
|
|
18
|
+
`CruisesUiMessagesProvider` and import only the locales your app supports.
|
|
19
|
+
|
|
20
|
+
```tsx
|
|
21
|
+
import { CruisesUiMessagesProvider } from "@voyantjs/cruises-ui"
|
|
22
|
+
import { cruisesUiEn } from "@voyantjs/cruises-ui/i18n/en"
|
|
23
|
+
import { cruisesUiRo } from "@voyantjs/cruises-ui/i18n/ro"
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
English-only apps should import only `./i18n/en`. Bilingual apps can import
|
|
27
|
+
`./i18n/en` and `./i18n/ro`.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"enrichment-program-list.d.ts","sourceRoot":"","sources":["../../src/components/enrichment-program-list.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"enrichment-program-list.d.ts","sourceRoot":"","sources":["../../src/components/enrichment-program-list.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAA;AAgBnC,MAAM,WAAW,0BAA2B,SAAQ,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC;IACtF,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;CAC7B;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,EACpC,SAAS,EACT,UAAU,EACV,SAAS,EACT,GAAG,KAAK,EACT,EAAE,0BAA0B,2CAkE5B"}
|
|
@@ -6,14 +6,7 @@ import { Badge } from "@voyantjs/ui/components/badge";
|
|
|
6
6
|
import { Card, CardContent } from "@voyantjs/ui/components/card";
|
|
7
7
|
import { cn } from "@voyantjs/ui/lib/utils";
|
|
8
8
|
import { Camera, Compass, GraduationCap, Mic, ScrollText, Sparkles } from "lucide-react";
|
|
9
|
-
|
|
10
|
-
naturalist: "Naturalist",
|
|
11
|
-
historian: "Historian",
|
|
12
|
-
photographer: "Photographer",
|
|
13
|
-
lecturer: "Lecturer",
|
|
14
|
-
expert: "Expert",
|
|
15
|
-
other: "Specialist",
|
|
16
|
-
};
|
|
9
|
+
import { useCruisesUiI18nOrDefault } from "../i18n";
|
|
17
10
|
const KIND_ICON = {
|
|
18
11
|
naturalist: Compass,
|
|
19
12
|
historian: ScrollText,
|
|
@@ -29,12 +22,14 @@ const KIND_ICON = {
|
|
|
29
22
|
* driven by `useEnrichmentMutation`.
|
|
30
23
|
*/
|
|
31
24
|
export function EnrichmentProgramList({ cruiseKey, emptyState, className, ...props }) {
|
|
25
|
+
const { messages } = useCruisesUiI18nOrDefault();
|
|
26
|
+
const m = messages.enrichmentProgramList;
|
|
32
27
|
const { data, isLoading } = useEnrichmentPrograms(cruiseKey);
|
|
33
28
|
if (isLoading) {
|
|
34
|
-
return (_jsx("div", { "data-slot": "enrichment-loading", className: cn("py-8 text-center", className), ...props, children: _jsx("p", { className: "text-muted-foreground", children:
|
|
29
|
+
return (_jsx("div", { "data-slot": "enrichment-loading", className: cn("py-8 text-center", className), ...props, children: _jsx("p", { className: "text-muted-foreground", children: m.loading }) }));
|
|
35
30
|
}
|
|
36
31
|
if (!data || data.length === 0) {
|
|
37
|
-
return (_jsx("div", { "data-slot": "enrichment-empty", className: cn("py-8 text-center", className), ...props, children: emptyState ??
|
|
32
|
+
return (_jsx("div", { "data-slot": "enrichment-empty", className: cn("py-8 text-center", className), ...props, children: emptyState ?? _jsx("p", { className: "text-muted-foreground", children: m.empty }) }));
|
|
38
33
|
}
|
|
39
34
|
return (_jsx("div", { "data-slot": "enrichment-list", className: cn("grid gap-4 sm:grid-cols-2", className), ...props, children: data.map((program) => {
|
|
40
35
|
const Icon = KIND_ICON[program.kind];
|
|
@@ -45,6 +40,6 @@ export function EnrichmentProgramList({ cruiseKey, emptyState, className, ...pro
|
|
|
45
40
|
.slice(0, 2)
|
|
46
41
|
.join("")
|
|
47
42
|
.toUpperCase();
|
|
48
|
-
return (_jsx(Card, { "data-slot": "enrichment-card", className: "overflow-hidden", children: _jsxs(CardContent, { className: "flex gap-4 p-4", children: [_jsxs(Avatar, { className: "size-14 shrink-0", children: [program.bioImageUrl ? (_jsx(AvatarImage, { src: program.bioImageUrl, alt: program.name })) : null, _jsx(AvatarFallback, { children: initials ||
|
|
43
|
+
return (_jsx(Card, { "data-slot": "enrichment-card", className: "overflow-hidden", children: _jsxs(CardContent, { className: "flex gap-4 p-4", children: [_jsxs(Avatar, { className: "size-14 shrink-0", children: [program.bioImageUrl ? (_jsx(AvatarImage, { src: program.bioImageUrl, alt: program.name })) : null, _jsx(AvatarFallback, { children: initials || m.avatarFallback })] }), _jsxs("div", { className: "min-w-0 flex-1 space-y-1", children: [_jsx("div", { className: "flex items-center gap-2", children: _jsxs(Badge, { variant: "secondary", className: "font-normal", children: [_jsx(Icon, { "aria-hidden": true, className: "mr-1 size-3" }), m.kindLabels[program.kind]] }) }), _jsx("div", { className: "font-semibold truncate", children: program.name }), program.title ? (_jsx("div", { className: "text-sm text-muted-foreground truncate", children: program.title })) : null, program.description ? (_jsx("p", { className: "text-sm text-muted-foreground line-clamp-3", children: program.description })) : null] })] }) }, program.id));
|
|
49
44
|
}) }));
|
|
50
45
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"external-badge.d.ts","sourceRoot":"","sources":["../../src/components/external-badge.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"external-badge.d.ts","sourceRoot":"","sources":["../../src/components/external-badge.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,EAAE,MAAM,+BAA+B,CAAA;AAErD,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAA;AAInC,MAAM,WAAW,wBAAyB,SAAQ,KAAK,CAAC,wBAAwB,CAAC,OAAO,KAAK,CAAC;IAC5F,6EAA6E;IAC7E,cAAc,EAAE,MAAM,CAAA;CACvB;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,cAAc,EACd,SAAS,EACT,GAAG,KAAK,EACT,EAAE,wBAAwB,2CAkB1B"}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { formatMessage } from "@voyantjs/i18n";
|
|
3
4
|
import { Badge } from "@voyantjs/ui/components/badge";
|
|
4
5
|
import { cn } from "@voyantjs/ui/lib/utils";
|
|
6
|
+
import { useCruisesUiI18nOrDefault } from "../i18n";
|
|
5
7
|
/**
|
|
6
8
|
* Small inline badge that surfaces cruise provenance to admin/storefront UIs.
|
|
7
9
|
* Renders as a subtle "External · <provider>" pill so operators (and shoppers,
|
|
@@ -9,5 +11,7 @@ import { cn } from "@voyantjs/ui/lib/utils";
|
|
|
9
11
|
* upstream and which is the operator's own.
|
|
10
12
|
*/
|
|
11
13
|
export function ExternalCruiseBadge({ sourceProvider, className, ...props }) {
|
|
12
|
-
|
|
14
|
+
const { messages } = useCruisesUiI18nOrDefault();
|
|
15
|
+
const m = messages.externalCruiseBadge;
|
|
16
|
+
return (_jsxs(Badge, { "data-slot": "external-cruise-badge", variant: "outline", className: cn("font-normal", className), title: formatMessage(m.title, { sourceProvider }), ...props, children: [_jsx("span", { "aria-hidden": "true", className: "mr-1", children: "\u2197" }), formatMessage(m.label, { sourceProvider })] }));
|
|
13
17
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pricing-grid.d.ts","sourceRoot":"","sources":["../../src/components/pricing-grid.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;
|
|
1
|
+
{"version":3,"file":"pricing-grid.d.ts","sourceRoot":"","sources":["../../src/components/pricing-grid.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;AAY1D,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAA;AAInC,MAAM,WAAW,gBAAiB,SAAQ,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC;IAC5E,uGAAuG;IACvG,MAAM,EAAE,WAAW,EAAE,CAAA;IACrB,+EAA+E;IAC/E,aAAa,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,MAAM,CAAA;IAC9C,+DAA+D;IAC/D,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAA;IAC1D,mGAAmG;IACnG,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAA;CAC5C;AAaD;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,EAC1B,MAAM,EACN,aAAa,EACb,WAAW,EACX,YAAY,EACZ,SAAS,EACT,GAAG,KAAK,EACT,EAAE,gBAAgB,2CAsGlB"}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { formatMessage } from "@voyantjs/i18n";
|
|
3
4
|
import { Badge } from "@voyantjs/ui/components/badge";
|
|
4
5
|
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@voyantjs/ui/components/table";
|
|
5
6
|
import { cn } from "@voyantjs/ui/lib/utils";
|
|
7
|
+
import { useCruisesUiI18nOrDefault } from "../i18n";
|
|
6
8
|
const AVAILABILITY_VARIANT = {
|
|
7
9
|
available: "default",
|
|
8
10
|
limited: "secondary",
|
|
@@ -10,19 +12,6 @@ const AVAILABILITY_VARIANT = {
|
|
|
10
12
|
wait_list: "outline",
|
|
11
13
|
sold_out: "destructive",
|
|
12
14
|
};
|
|
13
|
-
const AVAILABILITY_LABEL = {
|
|
14
|
-
available: "Available",
|
|
15
|
-
limited: "Limited",
|
|
16
|
-
on_request: "On request",
|
|
17
|
-
wait_list: "Wait list",
|
|
18
|
-
sold_out: "Sold out",
|
|
19
|
-
};
|
|
20
|
-
function defaultFormatPrice(amount, currency) {
|
|
21
|
-
const n = Number(amount);
|
|
22
|
-
if (!Number.isFinite(n))
|
|
23
|
-
return `${currency} ${amount}`;
|
|
24
|
-
return `${currency} ${n.toLocaleString(undefined, { maximumFractionDigits: 0 })}`;
|
|
25
|
-
}
|
|
26
15
|
/**
|
|
27
16
|
* The cabin × occupancy pricing matrix that's the heart of any cruise booking
|
|
28
17
|
* flow. Rows = cabin categories; columns = occupancy variants present in the
|
|
@@ -30,9 +19,16 @@ function defaultFormatPrice(amount, currency) {
|
|
|
30
19
|
* an availability badge and the price; clicking surfaces the underlying row
|
|
31
20
|
* for the booking flow to consume.
|
|
32
21
|
*/
|
|
33
|
-
export function PricingGrid({ prices, categoryLabel, formatPrice
|
|
22
|
+
export function PricingGrid({ prices, categoryLabel, formatPrice, onCellSelect, className, ...props }) {
|
|
23
|
+
const i18n = useCruisesUiI18nOrDefault();
|
|
24
|
+
const m = i18n.messages.pricingGrid;
|
|
25
|
+
const formatResolvedPrice = formatPrice ??
|
|
26
|
+
((amount, currency) => formatCruiseMoney(amount, currency, {
|
|
27
|
+
fallbackCurrencyAmount: i18n.messages.common.fallbackCurrencyAmount,
|
|
28
|
+
formatCurrency: i18n.formatCurrency,
|
|
29
|
+
}, { maximumFractionDigits: 0 }));
|
|
34
30
|
if (prices.length === 0) {
|
|
35
|
-
return (_jsx("div", { "data-slot": "pricing-grid-empty", className: cn("py-8 text-center", className), ...props, children: _jsx("p", { className: "text-muted-foreground", children:
|
|
31
|
+
return (_jsx("div", { "data-slot": "pricing-grid-empty", className: cn("py-8 text-center", className), ...props, children: _jsx("p", { className: "text-muted-foreground", children: m.empty }) }));
|
|
36
32
|
}
|
|
37
33
|
// Pivot the flat list into a (categoryId × occupancy) → cheapest price row.
|
|
38
34
|
const occupancies = Array.from(new Set(prices.map((p) => p.occupancy))).sort((a, b) => a - b);
|
|
@@ -48,15 +44,7 @@ export function PricingGrid({ prices, categoryLabel, formatPrice = defaultFormat
|
|
|
48
44
|
row.set(p.occupancy, p);
|
|
49
45
|
}
|
|
50
46
|
}
|
|
51
|
-
return (_jsx("div", { "data-slot": "pricing-grid", className: cn("overflow-x-auto", className), ...props, children: _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { className: "w-[220px]", children:
|
|
52
|
-
? "Single"
|
|
53
|
-
: occ === 2
|
|
54
|
-
? "Double"
|
|
55
|
-
: occ === 3
|
|
56
|
-
? "Triple"
|
|
57
|
-
: occ === 4
|
|
58
|
-
? "Quad"
|
|
59
|
-
: `${occ}-occupancy` }, occ)))] }) }), _jsx(TableBody, { children: Array.from(grouped.entries()).map(([categoryId, row]) => (_jsxs(TableRow, { children: [_jsx(TableCell, { className: "font-medium", children: categoryLabel?.(categoryId) ?? categoryId }), occupancies.map((occ) => {
|
|
47
|
+
return (_jsx("div", { "data-slot": "pricing-grid", className: cn("overflow-x-auto", className), ...props, children: _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { className: "w-[220px]", children: m.cabinCategory }), occupancies.map((occ) => (_jsx(TableHead, { className: "text-center", children: occupancyLabel(occ, i18n.messages.common.occupancyTableLabels) }, occ)))] }) }), _jsx(TableBody, { children: Array.from(grouped.entries()).map(([categoryId, row]) => (_jsxs(TableRow, { children: [_jsx(TableCell, { className: "font-medium", children: categoryLabel?.(categoryId) ?? categoryId }), occupancies.map((occ) => {
|
|
60
48
|
const price = row.get(occ);
|
|
61
49
|
if (!price) {
|
|
62
50
|
return (_jsx(TableCell, { className: "text-center text-muted-foreground", children: "\u2014" }, occ));
|
|
@@ -65,6 +53,29 @@ export function PricingGrid({ prices, categoryLabel, formatPrice = defaultFormat
|
|
|
65
53
|
price.availability !== "sold_out" &&
|
|
66
54
|
"cursor-pointer hover:bg-accent"), onClick: onCellSelect && price.availability !== "sold_out"
|
|
67
55
|
? () => onCellSelect(price)
|
|
68
|
-
: undefined, "data-slot": "pricing-grid-cell", children: [_jsx("div", { className: "font-semibold", children:
|
|
56
|
+
: undefined, "data-slot": "pricing-grid-cell", children: [_jsx("div", { className: "font-semibold", children: formatResolvedPrice(price.pricePerPerson, price.currency) }), _jsx("div", { className: "text-xs text-muted-foreground", children: m.perPerson }), _jsx(Badge, { variant: AVAILABILITY_VARIANT[price.availability], className: "mt-2 font-normal", children: m.availabilityLabels[price.availability] })] }, occ));
|
|
69
57
|
})] }, categoryId))) })] }) }));
|
|
70
58
|
}
|
|
59
|
+
function occupancyLabel(occupancy, labels) {
|
|
60
|
+
if (occupancy === 1)
|
|
61
|
+
return labels.single;
|
|
62
|
+
if (occupancy === 2)
|
|
63
|
+
return labels.double;
|
|
64
|
+
if (occupancy === 3)
|
|
65
|
+
return labels.triple;
|
|
66
|
+
if (occupancy === 4)
|
|
67
|
+
return labels.quad;
|
|
68
|
+
return formatMessage(labels.fallback, { count: occupancy });
|
|
69
|
+
}
|
|
70
|
+
function formatCruiseMoney(amount, currency, i18n, options) {
|
|
71
|
+
const n = Number(amount);
|
|
72
|
+
if (!Number.isFinite(n)) {
|
|
73
|
+
return formatMessage(i18n.fallbackCurrencyAmount, { currency, amount });
|
|
74
|
+
}
|
|
75
|
+
try {
|
|
76
|
+
return i18n.formatCurrency(n, currency, options);
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
return formatMessage(i18n.fallbackCurrencyAmount, { currency, amount });
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"quote-display.d.ts","sourceRoot":"","sources":["../../src/components/quote-display.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAA;
|
|
1
|
+
{"version":3,"file":"quote-display.d.ts","sourceRoot":"","sources":["../../src/components/quote-display.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAA;AAEpD,OAAO,EAAE,IAAI,EAA2B,MAAM,8BAA8B,CAAA;AAE5E,OAAO,KAAK,KAAK,KAAK,MAAM,OAAO,CAAA;AAMnC,MAAM,WAAW,iBAAkB,SAAQ,KAAK,CAAC,wBAAwB,CAAC,OAAO,IAAI,CAAC;IACpF,KAAK,EAAE,KAAK,CAAA;IACZ,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAA;CAC3D;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,iBAAiB,2CA4G1F"}
|
|
@@ -1,38 +1,45 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { formatMessage } from "@voyantjs/i18n";
|
|
3
4
|
import { Card, CardContent, CardHeader } from "@voyantjs/ui/components/card";
|
|
4
5
|
import { cn } from "@voyantjs/ui/lib/utils";
|
|
5
|
-
|
|
6
|
-
const negative = amount.startsWith("-");
|
|
7
|
-
const abs = negative ? amount.slice(1) : amount;
|
|
8
|
-
const n = Number(abs);
|
|
9
|
-
if (!Number.isFinite(n))
|
|
10
|
-
return `${currency} ${amount}`;
|
|
11
|
-
return `${negative ? "-" : ""}${currency} ${n.toLocaleString(undefined, {
|
|
12
|
-
minimumFractionDigits: 2,
|
|
13
|
-
maximumFractionDigits: 2,
|
|
14
|
-
})}`;
|
|
15
|
-
}
|
|
16
|
-
const COMPONENT_KIND_LABEL = {
|
|
17
|
-
gratuity: "Gratuity",
|
|
18
|
-
onboard_credit: "Onboard credit",
|
|
19
|
-
port_charge: "Port charges",
|
|
20
|
-
tax: "Tax",
|
|
21
|
-
ncf: "Non-comm fees",
|
|
22
|
-
airfare: "Airfare",
|
|
23
|
-
transfer: "Transfer",
|
|
24
|
-
insurance: "Insurance",
|
|
25
|
-
};
|
|
6
|
+
import { useCruisesUiI18nOrDefault } from "../i18n";
|
|
26
7
|
/**
|
|
27
8
|
* Renders an itemised cruise quote: base per person, components grouped by
|
|
28
9
|
* direction (additions / inclusions / credits), and totals. Mirrors what the
|
|
29
10
|
* server's composeQuote returns; pure presentational.
|
|
30
11
|
*/
|
|
31
|
-
export function QuoteDisplay({ quote, formatPrice
|
|
12
|
+
export function QuoteDisplay({ quote, formatPrice, className, ...props }) {
|
|
13
|
+
const i18n = useCruisesUiI18nOrDefault();
|
|
14
|
+
const m = i18n.messages.quoteDisplay;
|
|
15
|
+
const formatResolvedPrice = formatPrice ??
|
|
16
|
+
((amount, currency) => formatCruiseMoney(amount, currency, {
|
|
17
|
+
fallbackCurrencyAmount: i18n.messages.common.fallbackCurrencyAmount,
|
|
18
|
+
formatCurrency: i18n.formatCurrency,
|
|
19
|
+
}, {
|
|
20
|
+
minimumFractionDigits: 2,
|
|
21
|
+
maximumFractionDigits: 2,
|
|
22
|
+
}));
|
|
32
23
|
const additions = quote.components.filter((c) => c.direction === "addition");
|
|
33
24
|
const inclusions = quote.components.filter((c) => c.direction === "inclusion");
|
|
34
25
|
const credits = quote.components.filter((c) => c.direction === "credit");
|
|
35
|
-
|
|
26
|
+
const guestLabel = quote.guestCount === 1 ? m.guestLabelSingular : m.guestLabelPlural;
|
|
27
|
+
const occupancyLabel = formatMessage(m.occupancyCabin, { count: quote.occupancy });
|
|
28
|
+
return (_jsxs(Card, { "data-slot": "quote-display", className: cn(className), ...props, children: [_jsx(CardHeader, { children: _jsxs("div", { className: "flex items-baseline justify-between", children: [_jsxs("div", { children: [_jsx("h3", { className: "text-base font-semibold", children: m.heading }), quote.fareCodeName ? (_jsxs("p", { className: "text-sm text-muted-foreground", children: [quote.fareCodeName, quote.fareCode ? ` (${quote.fareCode})` : ""] })) : null] }), _jsxs("div", { className: "text-right", children: [_jsx("div", { className: "text-2xl font-bold", children: formatResolvedPrice(quote.totalForCabin, quote.currency) }), _jsx("div", { className: "text-xs text-muted-foreground", children: formatMessage(m.guestSummary, {
|
|
29
|
+
guestCount: quote.guestCount,
|
|
30
|
+
guestLabel,
|
|
31
|
+
occupancyLabel,
|
|
32
|
+
}) })] })] }) }), _jsxs(CardContent, { className: "space-y-4 text-sm", children: [_jsx(Row, { label: formatMessage(m.baseLine, {
|
|
33
|
+
price: formatResolvedPrice(quote.basePerPerson, quote.currency),
|
|
34
|
+
guestCount: quote.guestCount,
|
|
35
|
+
}) }), additions.length > 0 ? (_jsx(Section, { title: m.sections.additions, children: additions.map((c) => (_jsx(Row, { label: componentLabel(c, m), amount: `+ ${formatResolvedPrice(c.amount, c.currency)}` }, `add-${c.kind}-${c.label ?? ""}-${c.amount}`))) })) : null, credits.length > 0 ? (_jsx(Section, { title: m.sections.credits, children: credits.map((c) => (_jsx(Row, { label: componentLabel(c, m), amount: `− ${formatResolvedPrice(c.amount, c.currency)}`, amountClassName: "text-emerald-600" }, `cred-${c.kind}-${c.label ?? ""}-${c.amount}`))) })) : null, inclusions.length > 0 ? (_jsx(Section, { title: m.sections.included, children: inclusions.map((c) => (_jsx(Row, { label: c.label ?? m.componentKindLabels[c.kind], amount: m.includedAmount }, `inc-${c.kind}-${c.label ?? ""}`))) })) : null, _jsxs("div", { className: "border-t pt-3", children: [_jsx(Row, { label: m.totals.perPerson, amount: formatResolvedPrice(quote.totalPerPerson, quote.currency), amountClassName: "font-semibold" }), _jsx(Row, { label: m.totals.totalForCabin, amount: formatResolvedPrice(quote.totalForCabin, quote.currency), amountClassName: "font-bold text-base" })] })] })] }));
|
|
36
|
+
}
|
|
37
|
+
function componentLabel(component, messages) {
|
|
38
|
+
const baseLabel = component.label ?? messages.componentKindLabels[component.kind];
|
|
39
|
+
const scope = component.perPerson
|
|
40
|
+
? messages.componentScope.perPerson
|
|
41
|
+
: messages.componentScope.perCabin;
|
|
42
|
+
return `${baseLabel} (${scope})`;
|
|
36
43
|
}
|
|
37
44
|
function Section({ title, children }) {
|
|
38
45
|
return (_jsxs("div", { className: "space-y-1", children: [_jsx("h4", { className: "text-xs font-medium uppercase tracking-wider text-muted-foreground", children: title }), children] }));
|
|
@@ -40,3 +47,15 @@ function Section({ title, children }) {
|
|
|
40
47
|
function Row({ label, amount, amountClassName, }) {
|
|
41
48
|
return (_jsxs("div", { className: "flex items-baseline justify-between gap-4", children: [_jsx("span", { className: "text-muted-foreground", children: label }), amount ? _jsx("span", { className: cn("tabular-nums", amountClassName), children: amount }) : null] }));
|
|
42
49
|
}
|
|
50
|
+
function formatCruiseMoney(amount, currency, i18n, options) {
|
|
51
|
+
const n = Number(amount);
|
|
52
|
+
if (!Number.isFinite(n)) {
|
|
53
|
+
return formatMessage(i18n.fallbackCurrencyAmount, { currency, amount });
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
return i18n.formatCurrency(n, currency, options);
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return formatMessage(i18n.fallbackCurrencyAmount, { currency, amount });
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"en.d.ts","sourceRoot":"","sources":["../../src/i18n/en.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAEnD,eAAO,MAAM,WAAW,EAAE,iBAwEzB,CAAA"}
|
package/dist/i18n/en.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
export const cruisesUiEn = {
|
|
2
|
+
common: {
|
|
3
|
+
fallbackCurrencyAmount: "{currency} {amount}",
|
|
4
|
+
occupancyTableLabels: {
|
|
5
|
+
single: "Single",
|
|
6
|
+
double: "Double",
|
|
7
|
+
triple: "Triple",
|
|
8
|
+
quad: "Quad",
|
|
9
|
+
fallback: "{count}-occupancy",
|
|
10
|
+
},
|
|
11
|
+
},
|
|
12
|
+
enrichmentProgramList: {
|
|
13
|
+
loading: "Loading enrichment programs…",
|
|
14
|
+
empty: "No enrichment programs published for this cruise.",
|
|
15
|
+
avatarFallback: "?",
|
|
16
|
+
kindLabels: {
|
|
17
|
+
naturalist: "Naturalist",
|
|
18
|
+
historian: "Historian",
|
|
19
|
+
photographer: "Photographer",
|
|
20
|
+
lecturer: "Lecturer",
|
|
21
|
+
expert: "Expert",
|
|
22
|
+
other: "Specialist",
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
externalCruiseBadge: {
|
|
26
|
+
title: "Sourced via {sourceProvider}",
|
|
27
|
+
label: "External · {sourceProvider}",
|
|
28
|
+
},
|
|
29
|
+
pricingGrid: {
|
|
30
|
+
empty: "No pricing published for this sailing.",
|
|
31
|
+
cabinCategory: "Cabin category",
|
|
32
|
+
perPerson: "per person",
|
|
33
|
+
availabilityLabels: {
|
|
34
|
+
available: "Available",
|
|
35
|
+
limited: "Limited",
|
|
36
|
+
on_request: "On request",
|
|
37
|
+
wait_list: "Wait list",
|
|
38
|
+
sold_out: "Sold out",
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
quoteDisplay: {
|
|
42
|
+
heading: "Your quote",
|
|
43
|
+
guestLabelSingular: "guest",
|
|
44
|
+
guestLabelPlural: "guests",
|
|
45
|
+
occupancyCabin: "{count}-occupancy cabin",
|
|
46
|
+
guestSummary: "for {guestCount} {guestLabel} ({occupancyLabel})",
|
|
47
|
+
baseLine: "Base · {price} pp × {guestCount}",
|
|
48
|
+
sections: {
|
|
49
|
+
additions: "Additions",
|
|
50
|
+
credits: "Credits",
|
|
51
|
+
included: "Included",
|
|
52
|
+
},
|
|
53
|
+
componentKindLabels: {
|
|
54
|
+
gratuity: "Gratuity",
|
|
55
|
+
onboard_credit: "Onboard credit",
|
|
56
|
+
port_charge: "Port charges",
|
|
57
|
+
tax: "Tax",
|
|
58
|
+
ncf: "Non-comm fees",
|
|
59
|
+
airfare: "Airfare",
|
|
60
|
+
transfer: "Transfer",
|
|
61
|
+
insurance: "Insurance",
|
|
62
|
+
},
|
|
63
|
+
componentScope: {
|
|
64
|
+
perPerson: "per person",
|
|
65
|
+
perCabin: "per cabin",
|
|
66
|
+
},
|
|
67
|
+
includedAmount: "Included",
|
|
68
|
+
totals: {
|
|
69
|
+
perPerson: "Per person",
|
|
70
|
+
totalForCabin: "Total for cabin",
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { cruisesUiEn } from "./en";
|
|
2
|
+
export type { CruisePriceAvailability, CruiseQuoteComponentKind, CruisesUiMessages, EnrichmentProgramKind, } from "./messages";
|
|
3
|
+
export { type CruisesUiMessageOverrides, CruisesUiMessagesProvider, cruisesUiMessageDefinitions, getCruisesUiI18n, resolveCruisesUiMessages, useCruisesUiI18n, useCruisesUiI18nOrDefault, useCruisesUiMessages, useCruisesUiMessagesOrDefault, } from "./provider";
|
|
4
|
+
export { cruisesUiRo } from "./ro";
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/i18n/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,MAAM,CAAA;AAClC,YAAY,EACV,uBAAuB,EACvB,wBAAwB,EACxB,iBAAiB,EACjB,qBAAqB,GACtB,MAAM,YAAY,CAAA;AACnB,OAAO,EACL,KAAK,yBAAyB,EAC9B,yBAAyB,EACzB,2BAA2B,EAC3B,gBAAgB,EAChB,wBAAwB,EACxB,gBAAgB,EAChB,yBAAyB,EACzB,oBAAoB,EACpB,6BAA6B,GAC9B,MAAM,YAAY,CAAA;AACnB,OAAO,EAAE,WAAW,EAAE,MAAM,MAAM,CAAA"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { cruisesUiEn } from "./en";
|
|
2
|
+
export { CruisesUiMessagesProvider, cruisesUiMessageDefinitions, getCruisesUiI18n, resolveCruisesUiMessages, useCruisesUiI18n, useCruisesUiI18nOrDefault, useCruisesUiMessages, useCruisesUiMessagesOrDefault, } from "./provider";
|
|
3
|
+
export { cruisesUiRo } from "./ro";
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { EnrichmentProgramRecord, PriceRecord, Quote } from "@voyantjs/cruises-react";
|
|
2
|
+
export type EnrichmentProgramKind = EnrichmentProgramRecord["kind"];
|
|
3
|
+
export type CruisePriceAvailability = PriceRecord["availability"];
|
|
4
|
+
export type CruiseQuoteComponentKind = Quote["components"][number]["kind"];
|
|
5
|
+
export type CruisesUiMessages = {
|
|
6
|
+
common: {
|
|
7
|
+
fallbackCurrencyAmount: string;
|
|
8
|
+
occupancyTableLabels: {
|
|
9
|
+
single: string;
|
|
10
|
+
double: string;
|
|
11
|
+
triple: string;
|
|
12
|
+
quad: string;
|
|
13
|
+
fallback: string;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
enrichmentProgramList: {
|
|
17
|
+
loading: string;
|
|
18
|
+
empty: string;
|
|
19
|
+
avatarFallback: string;
|
|
20
|
+
kindLabels: Record<EnrichmentProgramKind, string>;
|
|
21
|
+
};
|
|
22
|
+
externalCruiseBadge: {
|
|
23
|
+
title: string;
|
|
24
|
+
label: string;
|
|
25
|
+
};
|
|
26
|
+
pricingGrid: {
|
|
27
|
+
empty: string;
|
|
28
|
+
cabinCategory: string;
|
|
29
|
+
perPerson: string;
|
|
30
|
+
availabilityLabels: Record<CruisePriceAvailability, string>;
|
|
31
|
+
};
|
|
32
|
+
quoteDisplay: {
|
|
33
|
+
heading: string;
|
|
34
|
+
guestLabelSingular: string;
|
|
35
|
+
guestLabelPlural: string;
|
|
36
|
+
occupancyCabin: string;
|
|
37
|
+
guestSummary: string;
|
|
38
|
+
baseLine: string;
|
|
39
|
+
sections: {
|
|
40
|
+
additions: string;
|
|
41
|
+
credits: string;
|
|
42
|
+
included: string;
|
|
43
|
+
};
|
|
44
|
+
componentKindLabels: Record<CruiseQuoteComponentKind, string>;
|
|
45
|
+
componentScope: {
|
|
46
|
+
perPerson: string;
|
|
47
|
+
perCabin: string;
|
|
48
|
+
};
|
|
49
|
+
includedAmount: string;
|
|
50
|
+
totals: {
|
|
51
|
+
perPerson: string;
|
|
52
|
+
totalForCabin: string;
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
};
|
|
56
|
+
//# sourceMappingURL=messages.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../src/i18n/messages.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAA;AAE1F,MAAM,MAAM,qBAAqB,GAAG,uBAAuB,CAAC,MAAM,CAAC,CAAA;AACnE,MAAM,MAAM,uBAAuB,GAAG,WAAW,CAAC,cAAc,CAAC,CAAA;AACjE,MAAM,MAAM,wBAAwB,GAAG,KAAK,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAA;AAE1E,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE;QACN,sBAAsB,EAAE,MAAM,CAAA;QAC9B,oBAAoB,EAAE;YACpB,MAAM,EAAE,MAAM,CAAA;YACd,MAAM,EAAE,MAAM,CAAA;YACd,MAAM,EAAE,MAAM,CAAA;YACd,IAAI,EAAE,MAAM,CAAA;YACZ,QAAQ,EAAE,MAAM,CAAA;SACjB,CAAA;KACF,CAAA;IACD,qBAAqB,EAAE;QACrB,OAAO,EAAE,MAAM,CAAA;QACf,KAAK,EAAE,MAAM,CAAA;QACb,cAAc,EAAE,MAAM,CAAA;QACtB,UAAU,EAAE,MAAM,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAA;KAClD,CAAA;IACD,mBAAmB,EAAE;QACnB,KAAK,EAAE,MAAM,CAAA;QACb,KAAK,EAAE,MAAM,CAAA;KACd,CAAA;IACD,WAAW,EAAE;QACX,KAAK,EAAE,MAAM,CAAA;QACb,aAAa,EAAE,MAAM,CAAA;QACrB,SAAS,EAAE,MAAM,CAAA;QACjB,kBAAkB,EAAE,MAAM,CAAC,uBAAuB,EAAE,MAAM,CAAC,CAAA;KAC5D,CAAA;IACD,YAAY,EAAE;QACZ,OAAO,EAAE,MAAM,CAAA;QACf,kBAAkB,EAAE,MAAM,CAAA;QAC1B,gBAAgB,EAAE,MAAM,CAAA;QACxB,cAAc,EAAE,MAAM,CAAA;QACtB,YAAY,EAAE,MAAM,CAAA;QACpB,QAAQ,EAAE,MAAM,CAAA;QAChB,QAAQ,EAAE;YACR,SAAS,EAAE,MAAM,CAAA;YACjB,OAAO,EAAE,MAAM,CAAA;YACf,QAAQ,EAAE,MAAM,CAAA;SACjB,CAAA;QACD,mBAAmB,EAAE,MAAM,CAAC,wBAAwB,EAAE,MAAM,CAAC,CAAA;QAC7D,cAAc,EAAE;YACd,SAAS,EAAE,MAAM,CAAA;YACjB,QAAQ,EAAE,MAAM,CAAA;SACjB,CAAA;QACD,cAAc,EAAE,MAAM,CAAA;QACtB,MAAM,EAAE;YACN,SAAS,EAAE,MAAM,CAAA;YACjB,aAAa,EAAE,MAAM,CAAA;SACtB,CAAA;KACF,CAAA;CACF,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type LocaleMessageOverrides, type PackageI18nValue } from "@voyantjs/i18n";
|
|
2
|
+
import type { ReactNode } from "react";
|
|
3
|
+
import type { CruisesUiMessages } from "./messages";
|
|
4
|
+
export declare const cruisesUiMessageDefinitions: {
|
|
5
|
+
en: CruisesUiMessages;
|
|
6
|
+
ro: CruisesUiMessages;
|
|
7
|
+
};
|
|
8
|
+
export type CruisesUiMessageOverrides = LocaleMessageOverrides<CruisesUiMessages>;
|
|
9
|
+
export declare function resolveCruisesUiMessages({ locale, overrides, }: {
|
|
10
|
+
locale: string | null | undefined;
|
|
11
|
+
overrides?: CruisesUiMessageOverrides | null;
|
|
12
|
+
}): CruisesUiMessages;
|
|
13
|
+
export declare function getCruisesUiI18n({ locale, overrides, }: {
|
|
14
|
+
locale?: string | null | undefined;
|
|
15
|
+
overrides?: CruisesUiMessageOverrides | null;
|
|
16
|
+
}): PackageI18nValue<CruisesUiMessages>;
|
|
17
|
+
export declare function CruisesUiMessagesProvider({ children, locale, overrides, }: {
|
|
18
|
+
children: ReactNode;
|
|
19
|
+
locale: string | null | undefined;
|
|
20
|
+
overrides?: CruisesUiMessageOverrides | null;
|
|
21
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
22
|
+
export declare const useCruisesUiI18n: () => PackageI18nValue<CruisesUiMessages>;
|
|
23
|
+
export declare const useCruisesUiMessages: () => CruisesUiMessages;
|
|
24
|
+
export declare function useCruisesUiI18nOrDefault(): PackageI18nValue<CruisesUiMessages>;
|
|
25
|
+
export declare function useCruisesUiMessagesOrDefault(): CruisesUiMessages;
|
|
26
|
+
//# sourceMappingURL=provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../src/i18n/provider.tsx"],"names":[],"mappings":"AAEA,OAAO,EAIL,KAAK,sBAAsB,EAC3B,KAAK,gBAAgB,EAEtB,MAAM,gBAAgB,CAAA;AACvB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAGtC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAKnD,eAAO,MAAM,2BAA2B;;;CAGe,CAAA;AAEvD,MAAM,MAAM,yBAAyB,GAAG,sBAAsB,CAAC,iBAAiB,CAAC,CAAA;AASjF,wBAAgB,wBAAwB,CAAC,EACvC,MAAM,EACN,SAAS,GACV,EAAE;IACD,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAA;IACjC,SAAS,CAAC,EAAE,yBAAyB,GAAG,IAAI,CAAA;CAC7C,qBAOA;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,MAAM,EACN,SAAS,GACV,EAAE;IACD,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAA;IAClC,SAAS,CAAC,EAAE,yBAAyB,GAAG,IAAI,CAAA;CAC7C,GAAG,gBAAgB,CAAC,iBAAiB,CAAC,CAStC;AAED,wBAAgB,yBAAyB,CAAC,EACxC,QAAQ,EACR,MAAM,EACN,SAAS,GACV,EAAE;IACD,QAAQ,EAAE,SAAS,CAAA;IACnB,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAA;IACjC,SAAS,CAAC,EAAE,yBAAyB,GAAG,IAAI,CAAA;CAC7C,2CAWA;AAED,eAAO,MAAM,gBAAgB,2CAA2B,CAAA;AACxD,eAAO,MAAM,oBAAoB,yBAA+B,CAAA;AAEhE,wBAAgB,yBAAyB,wCAExC;AAED,wBAAgB,6BAA6B,sBAE5C"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { createLocaleFormatters, createPackageMessagesContext, resolvePackageMessages, } from "@voyantjs/i18n";
|
|
4
|
+
import { cruisesUiEn } from "./en";
|
|
5
|
+
import { cruisesUiRo } from "./ro";
|
|
6
|
+
const fallbackLocale = "en";
|
|
7
|
+
export const cruisesUiMessageDefinitions = {
|
|
8
|
+
en: cruisesUiEn,
|
|
9
|
+
ro: cruisesUiRo,
|
|
10
|
+
};
|
|
11
|
+
const cruisesUiContext = createPackageMessagesContext("CruisesUiMessages");
|
|
12
|
+
const defaultCruisesUiI18n = {
|
|
13
|
+
messages: cruisesUiEn,
|
|
14
|
+
...createLocaleFormatters(fallbackLocale),
|
|
15
|
+
};
|
|
16
|
+
export function resolveCruisesUiMessages({ locale, overrides, }) {
|
|
17
|
+
return resolvePackageMessages({
|
|
18
|
+
definitions: cruisesUiMessageDefinitions,
|
|
19
|
+
fallbackLocale,
|
|
20
|
+
locale,
|
|
21
|
+
overrides,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
export function getCruisesUiI18n({ locale, overrides, }) {
|
|
25
|
+
const resolvedLocale = locale ?? fallbackLocale;
|
|
26
|
+
return {
|
|
27
|
+
messages: resolveCruisesUiMessages({
|
|
28
|
+
locale: resolvedLocale,
|
|
29
|
+
overrides,
|
|
30
|
+
}),
|
|
31
|
+
...createLocaleFormatters(resolvedLocale),
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export function CruisesUiMessagesProvider({ children, locale, overrides, }) {
|
|
35
|
+
return (_jsx(cruisesUiContext.ResolvedMessagesProvider, { definitions: cruisesUiMessageDefinitions, fallbackLocale: fallbackLocale, locale: locale, overrides: overrides, children: children }));
|
|
36
|
+
}
|
|
37
|
+
export const useCruisesUiI18n = cruisesUiContext.useI18n;
|
|
38
|
+
export const useCruisesUiMessages = cruisesUiContext.useMessages;
|
|
39
|
+
export function useCruisesUiI18nOrDefault() {
|
|
40
|
+
return cruisesUiContext.useOptionalI18n() ?? defaultCruisesUiI18n;
|
|
41
|
+
}
|
|
42
|
+
export function useCruisesUiMessagesOrDefault() {
|
|
43
|
+
return useCruisesUiI18nOrDefault().messages;
|
|
44
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ro.d.ts","sourceRoot":"","sources":["../../src/i18n/ro.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAEnD,eAAO,MAAM,WAAW,EAAE,iBAwEzB,CAAA"}
|
package/dist/i18n/ro.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
export const cruisesUiRo = {
|
|
2
|
+
common: {
|
|
3
|
+
fallbackCurrencyAmount: "{currency} {amount}",
|
|
4
|
+
occupancyTableLabels: {
|
|
5
|
+
single: "Single",
|
|
6
|
+
double: "Dubla",
|
|
7
|
+
triple: "Tripla",
|
|
8
|
+
quad: "Cvadrupla",
|
|
9
|
+
fallback: "{count} locuri",
|
|
10
|
+
},
|
|
11
|
+
},
|
|
12
|
+
enrichmentProgramList: {
|
|
13
|
+
loading: "Se incarca programele de imbogatire…",
|
|
14
|
+
empty: "Nu exista programe de imbogatire publicate pentru aceasta croaziera.",
|
|
15
|
+
avatarFallback: "?",
|
|
16
|
+
kindLabels: {
|
|
17
|
+
naturalist: "Naturalist",
|
|
18
|
+
historian: "Istoric",
|
|
19
|
+
photographer: "Fotograf",
|
|
20
|
+
lecturer: "Lector",
|
|
21
|
+
expert: "Expert",
|
|
22
|
+
other: "Specialist",
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
externalCruiseBadge: {
|
|
26
|
+
title: "Preluat prin {sourceProvider}",
|
|
27
|
+
label: "Extern · {sourceProvider}",
|
|
28
|
+
},
|
|
29
|
+
pricingGrid: {
|
|
30
|
+
empty: "Nu exista tarife publicate pentru aceasta plecare.",
|
|
31
|
+
cabinCategory: "Categoria cabinei",
|
|
32
|
+
perPerson: "de persoana",
|
|
33
|
+
availabilityLabels: {
|
|
34
|
+
available: "Disponibil",
|
|
35
|
+
limited: "Limitat",
|
|
36
|
+
on_request: "La cerere",
|
|
37
|
+
wait_list: "Lista de asteptare",
|
|
38
|
+
sold_out: "Epuizat",
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
quoteDisplay: {
|
|
42
|
+
heading: "Oferta ta",
|
|
43
|
+
guestLabelSingular: "pasager",
|
|
44
|
+
guestLabelPlural: "pasageri",
|
|
45
|
+
occupancyCabin: "cabina pentru {count} persoane",
|
|
46
|
+
guestSummary: "pentru {guestCount} {guestLabel} ({occupancyLabel})",
|
|
47
|
+
baseLine: "Baza · {price} pp × {guestCount}",
|
|
48
|
+
sections: {
|
|
49
|
+
additions: "Costuri suplimentare",
|
|
50
|
+
credits: "Credite",
|
|
51
|
+
included: "Inclus",
|
|
52
|
+
},
|
|
53
|
+
componentKindLabels: {
|
|
54
|
+
gratuity: "Bacsis",
|
|
55
|
+
onboard_credit: "Credit la bord",
|
|
56
|
+
port_charge: "Taxe portuare",
|
|
57
|
+
tax: "Taxa",
|
|
58
|
+
ncf: "Taxe necomisionabile",
|
|
59
|
+
airfare: "Bilet de avion",
|
|
60
|
+
transfer: "Transfer",
|
|
61
|
+
insurance: "Asigurare",
|
|
62
|
+
},
|
|
63
|
+
componentScope: {
|
|
64
|
+
perPerson: "de persoana",
|
|
65
|
+
perCabin: "pe cabina",
|
|
66
|
+
},
|
|
67
|
+
includedAmount: "Inclus",
|
|
68
|
+
totals: {
|
|
69
|
+
perPerson: "Per persoana",
|
|
70
|
+
totalForCabin: "Total pentru cabina",
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -2,4 +2,5 @@ export { EnrichmentProgramList, type EnrichmentProgramListProps, } from "./compo
|
|
|
2
2
|
export { ExternalCruiseBadge, type ExternalCruiseBadgeProps } from "./components/external-badge";
|
|
3
3
|
export { PricingGrid, type PricingGridProps } from "./components/pricing-grid";
|
|
4
4
|
export { QuoteDisplay, type QuoteDisplayProps } from "./components/quote-display";
|
|
5
|
+
export { type CruisePriceAvailability, type CruiseQuoteComponentKind, type CruisesUiMessageOverrides, type CruisesUiMessages, CruisesUiMessagesProvider, cruisesUiEn, cruisesUiMessageDefinitions, cruisesUiRo, type EnrichmentProgramKind, getCruisesUiI18n, resolveCruisesUiMessages, useCruisesUiI18n, useCruisesUiI18nOrDefault, useCruisesUiMessages, useCruisesUiMessagesOrDefault, } from "./i18n";
|
|
5
6
|
//# sourceMappingURL=index.d.ts.map
|
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,EACL,qBAAqB,EACrB,KAAK,0BAA0B,GAChC,MAAM,sCAAsC,CAAA;AAC7C,OAAO,EAAE,mBAAmB,EAAE,KAAK,wBAAwB,EAAE,MAAM,6BAA6B,CAAA;AAChG,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,2BAA2B,CAAA;AAC9E,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,4BAA4B,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,qBAAqB,EACrB,KAAK,0BAA0B,GAChC,MAAM,sCAAsC,CAAA;AAC7C,OAAO,EAAE,mBAAmB,EAAE,KAAK,wBAAwB,EAAE,MAAM,6BAA6B,CAAA;AAChG,OAAO,EAAE,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,2BAA2B,CAAA;AAC9E,OAAO,EAAE,YAAY,EAAE,KAAK,iBAAiB,EAAE,MAAM,4BAA4B,CAAA;AACjF,OAAO,EACL,KAAK,uBAAuB,EAC5B,KAAK,wBAAwB,EAC7B,KAAK,yBAAyB,EAC9B,KAAK,iBAAiB,EACtB,yBAAyB,EACzB,WAAW,EACX,2BAA2B,EAC3B,WAAW,EACX,KAAK,qBAAqB,EAC1B,gBAAgB,EAChB,wBAAwB,EACxB,gBAAgB,EAChB,yBAAyB,EACzB,oBAAoB,EACpB,6BAA6B,GAC9B,MAAM,QAAQ,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -2,3 +2,4 @@ export { EnrichmentProgramList, } from "./components/enrichment-program-list";
|
|
|
2
2
|
export { ExternalCruiseBadge } from "./components/external-badge";
|
|
3
3
|
export { PricingGrid } from "./components/pricing-grid";
|
|
4
4
|
export { QuoteDisplay } from "./components/quote-display";
|
|
5
|
+
export { CruisesUiMessagesProvider, cruisesUiEn, cruisesUiMessageDefinitions, cruisesUiRo, getCruisesUiI18n, resolveCruisesUiMessages, useCruisesUiI18n, useCruisesUiI18nOrDefault, useCruisesUiMessages, useCruisesUiMessagesOrDefault, } from "./i18n";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@voyantjs/cruises-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"license": "FSL-1.1-Apache-2.0",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -14,6 +14,18 @@
|
|
|
14
14
|
"types": "./dist/index.d.ts",
|
|
15
15
|
"import": "./dist/index.js"
|
|
16
16
|
},
|
|
17
|
+
"./i18n": {
|
|
18
|
+
"types": "./dist/i18n/index.d.ts",
|
|
19
|
+
"import": "./dist/i18n/index.js"
|
|
20
|
+
},
|
|
21
|
+
"./i18n/en": {
|
|
22
|
+
"types": "./dist/i18n/en.d.ts",
|
|
23
|
+
"import": "./dist/i18n/en.js"
|
|
24
|
+
},
|
|
25
|
+
"./i18n/ro": {
|
|
26
|
+
"types": "./dist/i18n/ro.d.ts",
|
|
27
|
+
"import": "./dist/i18n/ro.js"
|
|
28
|
+
},
|
|
17
29
|
"./components/*": {
|
|
18
30
|
"types": "./dist/components/*.d.ts",
|
|
19
31
|
"import": "./dist/components/*.js"
|
|
@@ -23,8 +35,11 @@
|
|
|
23
35
|
"@tanstack/react-query": "^5.0.0",
|
|
24
36
|
"react": "^19.0.0",
|
|
25
37
|
"react-dom": "^19.0.0",
|
|
26
|
-
"@voyantjs/cruises-react": "0.
|
|
27
|
-
"@voyantjs/ui": "0.
|
|
38
|
+
"@voyantjs/cruises-react": "0.17.0",
|
|
39
|
+
"@voyantjs/ui": "0.17.0"
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@voyantjs/i18n": "0.17.0"
|
|
28
43
|
},
|
|
29
44
|
"devDependencies": {
|
|
30
45
|
"@tanstack/react-query": "^5.96.2",
|
|
@@ -35,9 +50,10 @@
|
|
|
35
50
|
"react-dom": "^19.2.4",
|
|
36
51
|
"typescript": "^6.0.2",
|
|
37
52
|
"vitest": "^4.1.2",
|
|
38
|
-
"@voyantjs/cruises-react": "0.
|
|
53
|
+
"@voyantjs/cruises-react": "0.17.0",
|
|
54
|
+
"@voyantjs/i18n": "0.17.0",
|
|
39
55
|
"@voyantjs/voyant-typescript-config": "0.1.0",
|
|
40
|
-
"@voyantjs/ui": "0.
|
|
56
|
+
"@voyantjs/ui": "0.17.0"
|
|
41
57
|
},
|
|
42
58
|
"files": [
|
|
43
59
|
"dist"
|