@voyantjs/hospitality-ui 0.19.0 → 0.20.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.
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { CatalogSearchHit } from "@voyantjs/catalog-react";
|
|
2
|
+
export interface HotelCatalogCardProps {
|
|
3
|
+
hit: CatalogSearchHit;
|
|
4
|
+
onClick?: (hit: CatalogSearchHit) => void;
|
|
5
|
+
className?: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Search-result card for a hospitality (hotel / room type) hit. Reads:
|
|
9
|
+
* `name`, `propertyName`, `city`, `country`, `starRating`,
|
|
10
|
+
* `roomCapacity`, `nightlyRateFromCents`, `currency`, `tags`.
|
|
11
|
+
*/
|
|
12
|
+
export declare function HotelCatalogCard({ hit, onClick, className }: HotelCatalogCardProps): import("react/jsx-runtime").JSX.Element;
|
|
13
|
+
//# sourceMappingURL=hotel-catalog-card.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hotel-catalog-card.d.ts","sourceRoot":"","sources":["../../src/components/hotel-catalog-card.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAA;AAK/D,MAAM,WAAW,qBAAqB;IACpC,GAAG,EAAE,gBAAgB,CAAA;IACrB,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,gBAAgB,KAAK,IAAI,CAAA;IACzC,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,GAAG,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE,qBAAqB,2CA+DlF"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Badge } from "@voyantjs/ui/components/badge";
|
|
4
|
+
import { Card, CardContent } from "@voyantjs/ui/components/card";
|
|
5
|
+
import { cn } from "@voyantjs/ui/lib/utils";
|
|
6
|
+
/**
|
|
7
|
+
* Search-result card for a hospitality (hotel / room type) hit. Reads:
|
|
8
|
+
* `name`, `propertyName`, `city`, `country`, `starRating`,
|
|
9
|
+
* `roomCapacity`, `nightlyRateFromCents`, `currency`, `tags`.
|
|
10
|
+
*/
|
|
11
|
+
export function HotelCatalogCard({ hit, onClick, className }) {
|
|
12
|
+
const f = hit.document.fields;
|
|
13
|
+
const name = stringOr(f.name, "Untitled room");
|
|
14
|
+
const propertyName = stringOr(f.propertyName, null);
|
|
15
|
+
const city = stringOr(f.city, null);
|
|
16
|
+
const country = stringOr(f.country, null);
|
|
17
|
+
const starRating = numberOr(f.starRating, null);
|
|
18
|
+
const capacity = numberOr(f.roomCapacity, null);
|
|
19
|
+
const status = stringOr(f.status, null);
|
|
20
|
+
const tags = stringArray(f.tags);
|
|
21
|
+
const rate = numberOr(f.nightlyRateFromCents, null);
|
|
22
|
+
const currency = stringOr(f.currency ?? f.sellCurrency, null);
|
|
23
|
+
const rateLabel = rate != null && currency
|
|
24
|
+
? `${new Intl.NumberFormat(undefined, {
|
|
25
|
+
style: "currency",
|
|
26
|
+
currency,
|
|
27
|
+
maximumFractionDigits: 0,
|
|
28
|
+
}).format(rate / 100)} / night`
|
|
29
|
+
: null;
|
|
30
|
+
const location = [city, country].filter(Boolean).join(", ");
|
|
31
|
+
return (_jsx(Card, { className: cn("h-full cursor-pointer transition-colors hover:border-primary/40", onClick == null && "cursor-default", className), onClick: onClick ? () => onClick(hit) : undefined, children: _jsxs(CardContent, { className: "flex h-full flex-col gap-2 p-4", children: [_jsxs("div", { className: "flex items-start justify-between gap-2", children: [_jsxs("div", { className: "min-w-0", children: [_jsx("h3", { className: "line-clamp-2 font-medium text-sm", children: name }), propertyName && _jsx("p", { className: "text-muted-foreground text-xs", children: propertyName })] }), status && (_jsx(Badge, { variant: status === "active" ? "default" : "secondary", className: "shrink-0", children: status }))] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2 text-muted-foreground text-xs", children: [starRating != null && _jsx("span", { children: "★".repeat(Math.round(starRating)) }), location && _jsx("span", { children: location }), capacity != null && _jsxs("span", { children: ["\u00B7 sleeps ", capacity] }), rateLabel && _jsx("span", { className: "ml-auto font-medium text-foreground", children: rateLabel })] }), tags.length > 0 && (_jsx("div", { className: "mt-auto flex flex-wrap gap-1", children: tags.slice(0, 4).map((tag) => (_jsx(Badge, { variant: "outline", className: "text-[10px]", children: tag }, tag))) }))] }) }));
|
|
32
|
+
}
|
|
33
|
+
function stringOr(value, fallback) {
|
|
34
|
+
return typeof value === "string" && value.length > 0 ? value : fallback;
|
|
35
|
+
}
|
|
36
|
+
function numberOr(value, fallback) {
|
|
37
|
+
if (typeof value === "number")
|
|
38
|
+
return value;
|
|
39
|
+
if (typeof value === "string") {
|
|
40
|
+
const n = Number(value);
|
|
41
|
+
return Number.isFinite(n) ? n : fallback;
|
|
42
|
+
}
|
|
43
|
+
return fallback;
|
|
44
|
+
}
|
|
45
|
+
function stringArray(value) {
|
|
46
|
+
if (!Array.isArray(value))
|
|
47
|
+
return [];
|
|
48
|
+
return value.filter((v) => typeof v === "string" && v.length > 0);
|
|
49
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@voyantjs/hospitality-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -15,6 +15,9 @@
|
|
|
15
15
|
"import": "./dist/index.js",
|
|
16
16
|
"default": "./dist/index.js"
|
|
17
17
|
},
|
|
18
|
+
"./styles.css": {
|
|
19
|
+
"default": "./src/styles.css"
|
|
20
|
+
},
|
|
18
21
|
"./i18n": {
|
|
19
22
|
"types": "./dist/i18n/index.d.ts",
|
|
20
23
|
"import": "./dist/i18n/index.js",
|
|
@@ -42,9 +45,10 @@
|
|
|
42
45
|
"react-dom": "^19.0.0",
|
|
43
46
|
"react-hook-form": "^7.60.0",
|
|
44
47
|
"zod": "^4.3.6",
|
|
45
|
-
"@voyantjs/
|
|
46
|
-
"@voyantjs/
|
|
47
|
-
"@voyantjs/
|
|
48
|
+
"@voyantjs/catalog-react": "0.20.0",
|
|
49
|
+
"@voyantjs/hospitality-react": "0.20.0",
|
|
50
|
+
"@voyantjs/pricing-react": "0.20.0",
|
|
51
|
+
"@voyantjs/ui": "0.20.0"
|
|
48
52
|
},
|
|
49
53
|
"devDependencies": {
|
|
50
54
|
"@tanstack/react-query": "^5.96.2",
|
|
@@ -57,17 +61,19 @@
|
|
|
57
61
|
"typescript": "^6.0.2",
|
|
58
62
|
"vitest": "^4.1.2",
|
|
59
63
|
"zod": "^4.3.6",
|
|
60
|
-
"@voyantjs/
|
|
61
|
-
"@voyantjs/
|
|
62
|
-
"@voyantjs/
|
|
63
|
-
"@voyantjs/
|
|
64
|
-
"@voyantjs/ui": "0.
|
|
64
|
+
"@voyantjs/catalog-react": "0.20.0",
|
|
65
|
+
"@voyantjs/hospitality-react": "0.20.0",
|
|
66
|
+
"@voyantjs/i18n": "0.20.0",
|
|
67
|
+
"@voyantjs/pricing-react": "0.20.0",
|
|
68
|
+
"@voyantjs/ui": "0.20.0",
|
|
69
|
+
"@voyantjs/voyant-typescript-config": "0.1.0"
|
|
65
70
|
},
|
|
66
71
|
"dependencies": {
|
|
67
|
-
"@voyantjs/i18n": "0.
|
|
72
|
+
"@voyantjs/i18n": "0.20.0"
|
|
68
73
|
},
|
|
69
74
|
"files": [
|
|
70
|
-
"dist"
|
|
75
|
+
"dist",
|
|
76
|
+
"src/styles.css"
|
|
71
77
|
],
|
|
72
78
|
"publishConfig": {
|
|
73
79
|
"access": "public"
|
package/src/styles.css
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/* Tailwind v4 source-detection helper.
|
|
2
|
+
*
|
|
3
|
+
* Templates that consume this package should `@import` this CSS so Tailwind
|
|
4
|
+
* scans these component files for utility classes:
|
|
5
|
+
*
|
|
6
|
+
* @import "@voyantjs/<domain>-ui/styles.css";
|
|
7
|
+
*
|
|
8
|
+
* Without it, classes that only appear inside this package (e.g. data-attr
|
|
9
|
+
* variants on primitives) won't be compiled into the final bundle.
|
|
10
|
+
*/
|
|
11
|
+
@source "./components/**/*.{ts,tsx}";
|