@forgebuild/hotal-ui 0.23.48 → 0.23.80

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.
Files changed (3) hide show
  1. package/dist/hotal-ui.js +11693 -10099
  2. package/dist/index.d.ts +260 -0
  3. package/package.json +1 -1
package/dist/index.d.ts CHANGED
@@ -111,9 +111,16 @@ export interface BookingFlowElement extends HTMLElement {
111
111
  }
112
112
 
113
113
  export interface MenuItem {
114
+ /** Needed to add this item to a cart (see MenuSectionElement.cartEnabled)
115
+ * -- an item missing this never gets a quantity stepper. */
116
+ id?: string;
114
117
  name: string;
115
118
  description?: string;
116
119
  price?: string;
120
+ /** Numeric price for cart math, independent of `price` above (which may
121
+ * be an arbitrary display override a cart can't add up) -- like `id`,
122
+ * required for this item to get a stepper. */
123
+ priceValue?: number;
117
124
  tagline?: string;
118
125
  group?: string;
119
126
  country?: string;
@@ -125,6 +132,18 @@ export interface MenuCategory {
125
132
  items: MenuItem[];
126
133
  }
127
134
 
135
+ export interface MenuCartItem {
136
+ id: string;
137
+ name: string;
138
+ price: number;
139
+ quantity: number;
140
+ }
141
+
142
+ export interface MenuSectionCartChangeEventDetail {
143
+ items: MenuCartItem[];
144
+ total: number;
145
+ }
146
+
128
147
  export interface MenuInfoItem {
129
148
  label: string;
130
149
  value: string;
@@ -156,6 +175,16 @@ export interface MenuSectionElement extends HTMLElement {
156
175
  countryFilter?: boolean;
157
176
  groupFilterAllLabel?: string;
158
177
  countryFilterAllLabel?: string;
178
+ /** Adds a +/- stepper next to any item with both `id` and `priceValue`
179
+ * (classic variant only, not `panel`) and emits `cart-change` on every
180
+ * change. Off by default -- most hosts use this purely as a read-only
181
+ * display. */
182
+ cartEnabled?: boolean;
183
+ /** Restore cart quantities, e.g. from localStorage: [{ id, quantity }].
184
+ * Reactive, not mount-only -- reassign it (a new array) to re-sync
185
+ * quantities a host changed elsewhere, same as
186
+ * ProshopCatalogueElement's own `initial`. */
187
+ initial?: { id: string; quantity: number }[];
159
188
  }
160
189
 
161
190
  export interface EventInquiry {
@@ -366,6 +395,10 @@ export interface SpaBookingWidgetElement extends HTMLElement {
366
395
  /** Shown as a "Staff" row in the summary panel once a service is picked.
367
396
  * Purely presentational -- there is no staff picker. Omit to hide the row. */
368
397
  staffLabel?: string;
398
+ /** Hides every price display (service list, summary rows, total) -- for
399
+ * a free service (e.g. a consultation call) where showing "£0.00"
400
+ * throughout would read oddly. */
401
+ hidePrice?: boolean;
369
402
  /** JS `Date#getDay()` values (0 = Sunday) to gray out on the calendar,
370
403
  * e.g. `[2]` for a Tuesday closure. Visual hint only -- fetchAvailability
371
404
  * is the real source of truth for what's bookable. */
@@ -698,6 +731,64 @@ export interface LiveDiningOrdersFeedElement extends HTMLElement {
698
731
  filterBookingId?: string;
699
732
  }
700
733
 
734
+ export interface ShopOrderItem {
735
+ id?: string;
736
+ item_id?: string;
737
+ item_name: string;
738
+ quantity: number;
739
+ unit_price?: string | number;
740
+ total?: string | number;
741
+ /** e.g. size/color ("M / Navy") for apparel-style items. */
742
+ variant?: string | null;
743
+ }
744
+
745
+ export interface ShopOrder {
746
+ id: string;
747
+ booking_id?: string | null;
748
+ guest_id?: string | null;
749
+ status: string;
750
+ notes?: string | null;
751
+ fulfillment_method?: 'pickup' | 'shipping' | null;
752
+ total_price: string | number;
753
+ created_at: string;
754
+ items: ShopOrderItem[];
755
+ }
756
+
757
+ export interface ShopOrderBookingRef {
758
+ id: string;
759
+ room_number?: string | null;
760
+ guest_name?: string | null;
761
+ }
762
+
763
+ export interface LiveShopOrdersFeedElement extends HTMLElement {
764
+ heading?: string;
765
+ channelName?: string;
766
+ ablyAuthUrl?: string;
767
+ /** Fetches one page of order history, newest-first; Ably only delivers
768
+ * events published after the subscription starts, so history has to
769
+ * come from a REST call. Omit `cursor` for the first page; each
770
+ * subsequent call passes the `created_at` of the oldest loaded order so
771
+ * only the next page is fetched. Return fewer than `limit` items to
772
+ * signal there's nothing older left. */
773
+ fetchOrders?: (params: { cursor?: string; limit: number }) => Promise<ShopOrder[]>;
774
+ /** Resolves booking_id -> room/guest, since the order payload carries
775
+ * only the id. */
776
+ bookings?: ShopOrderBookingRef[];
777
+ maxItems?: number;
778
+ /** Set false to stop fetching further pages once the first one loads --
779
+ * disables the scroll-to-bottom sentinel entirely rather than swapping
780
+ * in a manual button. Default true. */
781
+ autoLoadMore?: boolean;
782
+ emptyLabel?: string;
783
+ /** Symbol prefixed to money amounts ('€', '$', ...); the payload carries
784
+ * no currency of its own. Default empty (bare amount). */
785
+ currency?: string;
786
+ /** Restricts the feed to one booking's orders -- `fetchOrders` should
787
+ * already be scoped server-side; this only filters the live `new-order`
788
+ * events, which otherwise arrive unfiltered off the channel. */
789
+ filterBookingId?: string;
790
+ }
791
+
701
792
  export interface VenueCarouselSlide {
702
793
  key: string;
703
794
  src: string;
@@ -957,6 +1048,9 @@ export interface BookingCheckoutElement extends HTMLElement {
957
1048
  locations?: BookingCheckoutLocation[];
958
1049
  defaultPhoneCode?: string;
959
1050
  allowBookingForOther?: boolean;
1051
+ /** Hides the "Booking for myself/someone else" toggle entirely (not just
1052
+ * the "other" option) -- for checkouts that aren't a booking at all. */
1053
+ showBookingFor?: boolean;
960
1054
  termsHref?: string;
961
1055
  prefill?: BookingCheckoutPrefill;
962
1056
  resolveInitialLocation?: () => Promise<BookingCheckoutResolvedLocation | null>;
@@ -1161,6 +1255,11 @@ export interface GoogleReviewItem {
1161
1255
  text: string;
1162
1256
  relative_time?: string;
1163
1257
  published_at?: string | null;
1258
+ /** Photos the reviewer attached to their review (not the same as
1259
+ * author_photo, which is their profile picture). Only ever populated
1260
+ * when the host's fetchReviews is backed by SerpApi's Google Maps
1261
+ * Reviews API -- Google's own official Places API never exposes these. */
1262
+ photos?: string[] | null;
1164
1263
  }
1165
1264
 
1166
1265
  /** The payload <google-reviews> renders -- the shape served by the OTA's
@@ -1192,10 +1291,62 @@ export interface GoogleReviewsElement extends HTMLElement {
1192
1291
  /** Slider only: seconds between automatic advances; 0/unset = off. */
1193
1292
  autoAdvance?: number;
1194
1293
  maxReviews?: number;
1294
+ /** Sweeps a light band across the CTA on hover instead of the default
1295
+ * opacity dip. Off by default. */
1296
+ ctaShimmer?: boolean;
1195
1297
  strings?: Partial<GoogleReviewsStrings>;
1196
1298
  fetchReviews?: () => Promise<GoogleReviewsPayload>;
1197
1299
  }
1198
1300
 
1301
+ export interface TripadvisorReviewItem {
1302
+ author: string;
1303
+ author_photo?: string | null;
1304
+ rating: number;
1305
+ text: string;
1306
+ relative_time?: string;
1307
+ published_at?: string | null;
1308
+ /** Photos the reviewer attached to their review. */
1309
+ photos?: string[] | null;
1310
+ }
1311
+
1312
+ /** The payload <tripadvisor-reviews> renders. Same shape as
1313
+ * GoogleReviewsPayload (maps_url/write_review_url renamed to place_url,
1314
+ * since Tripadvisor has no equivalent one-click write-review deep link). */
1315
+ export interface TripadvisorReviewsPayload {
1316
+ rating: number | null;
1317
+ count: number;
1318
+ place_url?: string | null;
1319
+ write_review_url?: string | null;
1320
+ reviews: TripadvisorReviewItem[];
1321
+ fetched_at?: string;
1322
+ }
1323
+
1324
+ export interface TripadvisorReviewsStrings {
1325
+ reviewsLabel: string;
1326
+ ctaLabel: string;
1327
+ seeAllLabel: string;
1328
+ loadingLabel: string;
1329
+ errorLabel: string;
1330
+ }
1331
+
1332
+ /** <tripadvisor-reviews>: live Tripadvisor rating, count, and the latest
1333
+ * review cards. Same shape and behaviour as <google-reviews> (see its own
1334
+ * doc comment) -- a separate component rather than a shared one because
1335
+ * each review source gets its own summary doc, seed script, and branding.
1336
+ * Theme via --ta-*. */
1337
+ export interface TripadvisorReviewsElement extends HTMLElement {
1338
+ /** 'grid' (default) or 'slider' (scroll-snap track with prev/next arrows). */
1339
+ variant?: 'grid' | 'slider';
1340
+ /** Slider only: seconds between automatic advances; 0/unset = off. */
1341
+ autoAdvance?: number;
1342
+ maxReviews?: number;
1343
+ /** Sweeps a light band across the CTA on hover instead of the default
1344
+ * opacity dip. Off by default. */
1345
+ ctaShimmer?: boolean;
1346
+ strings?: Partial<TripadvisorReviewsStrings>;
1347
+ fetchReviews?: () => Promise<TripadvisorReviewsPayload>;
1348
+ }
1349
+
1199
1350
  /** <ai-chat-widget>: a floating chat launcher + panel that talks to a
1200
1351
  * host-provided chat API endpoint (any AI SDK v5+ UI-message-stream route).
1201
1352
  * Knows nothing about the model, tools, or business data behind `api` --
@@ -1214,10 +1365,116 @@ export interface AiChatWidgetElement extends HTMLElement {
1214
1365
  phone?: string;
1215
1366
  }
1216
1367
 
1368
+ /** One purchasable option within a variant group -- a real, distinct
1369
+ * catalogue item (own id, own price, own stock on the host's backend), just
1370
+ * presented as a pill under the group's single card instead of a separate
1371
+ * card of its own. */
1372
+ export interface ProshopVariant {
1373
+ id: string;
1374
+ /** e.g. "Blue", "Small" -- shown on the pill. */
1375
+ label: string;
1376
+ price: number | null;
1377
+ }
1378
+
1379
+ export interface ProshopItem {
1380
+ id: string;
1381
+ name: string;
1382
+ description?: string;
1383
+ category?: string;
1384
+ image?: string | null;
1385
+ price: number | null;
1386
+ /** When set, the photo + name link here (e.g. a product detail page).
1387
+ * Omit for a catalogue with no such page -- the card stays static. */
1388
+ href?: string | null;
1389
+ /** Renders this card as a variant group: a pill per entry (e.g. colors or
1390
+ * sizes) beneath the description, switching which real item id/price the
1391
+ * qty stepper and cart operate on. `id`/`price` above are only the
1392
+ * initial (first-variant) display values. Each entry is a genuinely
1393
+ * separate catalogue item, so per-variant stock still works with no
1394
+ * other change -- the host just supplies N real items grouped under one
1395
+ * display card instead of N separate cards. */
1396
+ variants?: ProshopVariant[];
1397
+ }
1398
+
1399
+ export interface ProshopCartItem {
1400
+ id: string;
1401
+ name: string;
1402
+ price: number;
1403
+ quantity: number;
1404
+ }
1405
+
1406
+ export interface ProshopCatalogueChangeEventDetail {
1407
+ items: ProshopCartItem[];
1408
+ total: number;
1409
+ }
1410
+
1411
+ /** <proshop-catalogue>: browse + cart-build a shop/pro-shop catalogue (merch,
1412
+ * equipment, etc.) -- category tabs, a card grid (photo/name/price/qty
1413
+ * stepper), and a sticky cart-total bar. Stops at the cart on purpose:
1414
+ * checkout (contact details + Stripe payment) is <booking-checkout>'s job --
1415
+ * feed this component's `cart-change` total/items into that one rather than
1416
+ * building a second payment flow. Emits `cart-change` on every quantity
1417
+ * change. Theme via --pc-*. */
1418
+ export interface ProshopCatalogueElement extends HTMLElement {
1419
+ items?: ProshopItem[];
1420
+ currency?: string;
1421
+ /** Explicit category display order; categories not listed here are
1422
+ * appended in first-seen order. Omit to use first-seen order only. */
1423
+ categoryOrder?: string[];
1424
+ /** Overrides the auto-generated label for a category key -- otherwise a
1425
+ * category key is shown verbatim. */
1426
+ categoryLabels?: Record<string, string>;
1427
+ /** Restore cart quantities, e.g. from localStorage: [{ id, quantity }].
1428
+ * Reactive, not mount-only -- reassign it (a new array) after mount to
1429
+ * re-sync quantities a host changed elsewhere, e.g. a checkout sidebar. */
1430
+ initial?: { id: string; quantity: number }[];
1431
+ }
1432
+
1433
+ export interface InstagramFeedPost {
1434
+ id: string;
1435
+ image: string;
1436
+ caption?: string | null;
1437
+ permalink?: string | null;
1438
+ likeCount?: number | null;
1439
+ commentCount?: number | null;
1440
+ }
1441
+
1442
+ export interface InstagramFeedStrings {
1443
+ followersLabel: string;
1444
+ viewProfileLabel: string;
1445
+ emptyLabel: string;
1446
+ }
1447
+
1448
+ /** <instagram-feed>: a curated grid/slider of Instagram posts plus a
1449
+ * follower-count header and "View profile" link. Unlike GoogleReviews/
1450
+ * TripadvisorReviews, `posts` arrives as a plain prop, not a fetchX
1451
+ * callback -- there's no live per-request fetch to make, so no loading/
1452
+ * error state. Same slider mechanics (continuous loop, auto-advance,
1453
+ * pause on hover/focus) as those two for consistency. No heading/eyebrow
1454
+ * of its own -- that's host-page copy, same split as <google-reviews>.
1455
+ * Theme via --if-*. */
1456
+ export interface InstagramFeedElement extends HTMLElement {
1457
+ posts?: InstagramFeedPost[];
1458
+ /** Shown as the header stat, e.g. "4.3K Followers". Omit to hide the stat entirely. */
1459
+ followers?: number;
1460
+ /** The real instagram.com/<handle> URL -- the "View profile" link, and a
1461
+ * post's link fallback when it has no permalink of its own. */
1462
+ profileUrl?: string;
1463
+ /** 'grid' (default) or 'slider' (scroll-snap track with prev/next arrows). */
1464
+ variant?: 'grid' | 'slider';
1465
+ /** Slider only: seconds between automatic advances; 0/unset = off. */
1466
+ autoAdvance?: number;
1467
+ /** Cap on rendered post tiles. Default 12. */
1468
+ maxPosts?: number;
1469
+ strings?: Partial<InstagramFeedStrings>;
1470
+ }
1471
+
1217
1472
  declare global {
1218
1473
  interface HTMLElementTagNameMap {
1219
1474
  'ai-chat-widget': AiChatWidgetElement;
1220
1475
  'google-reviews': GoogleReviewsElement;
1476
+ 'tripadvisor-reviews': TripadvisorReviewsElement;
1477
+ 'instagram-feed': InstagramFeedElement;
1221
1478
  'service-booking-bar': ServiceBookingBarElement;
1222
1479
  'reservation-bar': ReservationBarElement;
1223
1480
  'booking-flow': BookingFlowElement;
@@ -1231,6 +1488,7 @@ declare global {
1231
1488
  'live-spa-bookings-feed': LiveSpaBookingsFeedElement;
1232
1489
  'live-golf-bookings-feed': LiveGolfBookingsFeedElement;
1233
1490
  'live-dining-orders-feed': LiveDiningOrdersFeedElement;
1491
+ 'live-shop-orders-feed': LiveShopOrdersFeedElement;
1234
1492
  'venue-carousel': VenueCarouselElement;
1235
1493
  'room-grid': RoomGridElement;
1236
1494
  'island-map': IslandMapElement;
@@ -1247,6 +1505,7 @@ declare global {
1247
1505
  'searchable-select': SearchableSelectElement;
1248
1506
  'booking-confirmed': BookingConfirmedElement;
1249
1507
  'restaurant-order-form': RestaurantOrderFormElement;
1508
+ 'proshop-catalogue': ProshopCatalogueElement;
1250
1509
  }
1251
1510
 
1252
1511
  interface HTMLElementEventMap {
@@ -1267,6 +1526,7 @@ declare global {
1267
1526
  'checkout-confirmed': CustomEvent<{ ref?: string }>;
1268
1527
  ready: CustomEvent<undefined>;
1269
1528
  'extras-change': CustomEvent<ExtrasCatalogueChangeEventDetail>;
1529
+ 'cart-change': CustomEvent<ProshopCatalogueChangeEventDetail>;
1270
1530
  'order-placed': CustomEvent<RestaurantOrderPlacedEventDetail>;
1271
1531
  'date-filter-change': CustomEvent<LiveReservationsFeedDateFilterEventDetail>;
1272
1532
  // Not declared here: `searchable-select` dispatches a CustomEvent named
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forgebuild/hotal-ui",
3
- "version": "0.23.48",
3
+ "version": "0.23.80",
4
4
  "description": "Reusable Svelte UI components for booking and reservation experiences.",
5
5
  "type": "module",
6
6
  "license": "MIT",