# Booking Flow Components (/developers/web-components-booking)



These components handle the interactive booking steps. The duration selector uses data you provide; the calendar, time slots, and add-on selector fetch data from the API when you call `.load()`.

Duration Selector [#duration-selector]

A button group for choosing a booking duration.

```html
<resytech-duration-selector show-label="true" label-text="Choose Duration"></resytech-duration-selector>
```

Set the durations from your selected activity:

```javascript
document.addEventListener('equipment-select', (e) => {
  const selector = document.querySelector('resytech-duration-selector');
  selector.durations = selectedActivity.durations;
});
```

Attributes [#attributes]

| Attribute    | Type      | Default    | Description                       |
| ------------ | --------- | ---------- | --------------------------------- |
| `selected`   | `string`  | —          | Duration UUID to show as selected |
| `show-label` | `boolean` | `true`     | Show heading label                |
| `label-text` | `string`  | `Duration` | Label text                        |

Events [#events]

| Event             | Detail                   |
| ----------------- | ------------------------ |
| `duration-select` | `{ duration: Duration }` |

***

Calendar [#calendar]

Month calendar with availability and optional per-day pricing. Fetches data from the API.

```html
<resytech-calendar
  activity-id="uuid"
  duration-id="uuid"
  show-prices="true">
</resytech-calendar>
```

The calendar auto-loads when `activity-id` is present on connect. For dynamic use, set properties and call `.load()`:

```javascript
const calendar = document.querySelector('resytech-calendar');
calendar.activityId = selectedActivity.uuid;
calendar.durationId = selectedDuration.uuid;
await calendar.load();
```

Attributes [#attributes-1]

| Attribute           | Type      | Default         | Description                                             |
| ------------------- | --------- | --------------- | ------------------------------------------------------- |
| `activity-id`       | `string`  | —               | Activity UUID (required for loading)                    |
| `duration-id`       | `string`  | —               | Duration UUID for pricing                               |
| `duration-mins`     | `number`  | —               | Duration in minutes (alternative to `duration-id`)      |
| `cart-id`           | `string`  | —               | Cart ID for cart-aware availability                     |
| `selected`          | `string`  | —               | Pre-selected date (`YYYY-MM-DD`)                        |
| `show-prices`       | `boolean` | `false`         | Show per-day pricing                                    |
| `show-label`        | `boolean` | `true`          | Show heading label                                      |
| `label-text`        | `string`  | `Select a Date` | Label text                                              |
| `use-cart-calendar` | `boolean` | `false`         | Use cart calendar endpoint instead of activity calendar |

Events [#events-1]

| Event         | Detail                                       |
| ------------- | -------------------------------------------- |
| `date-select` | `{ date: string, day: ActivityCalendarDay }` |

Month navigation (‹ ›) automatically re-fetches data. The calendar maintains its layout during loading — no height shift.

***

Time Slots [#time-slots]

Grid of available time slots with price and optional remaining seats. Fetches from the API.

```html
<resytech-timeslots
  activity-id="uuid"
  date="2025-06-15"
  duration-id="uuid">
</resytech-timeslots>
```

Typically loaded in response to a date selection:

```javascript
document.addEventListener('date-select', async (e) => {
  const slots = document.querySelector('resytech-timeslots');
  slots.activityId = selectedActivity.uuid;
  slots.date = e.detail.date;
  slots.durationId = selectedDuration.uuid;
  await slots.load();
});
```

Attributes [#attributes-2]

| Attribute             | Type      | Default         | Description                            |
| --------------------- | --------- | --------------- | -------------------------------------- |
| `activity-id`         | `string`  | —               | Activity UUID                          |
| `date`                | `string`  | —               | Date in `YYYY-MM-DD` format (required) |
| `duration-id`         | `string`  | —               | Duration UUID                          |
| `duration-mins`       | `number`  | —               | Duration in minutes (alternative)      |
| `selected`            | `number`  | —               | Selected slot index                    |
| `show-label`          | `boolean` | `true`          | Show heading label                     |
| `label-text`          | `string`  | `Select a Time` | Label text                             |
| `show-seats`          | `boolean` | `true`          | Show remaining seat count              |
| `low-seats-threshold` | `number`  | `5`             | Seats below this number show in red    |

Events [#events-2]

| Event             | Detail                       |
| ----------------- | ---------------------------- |
| `timeslot-select` | `{ slot: ActivityTimeSlot }` |

***

Add-on Selector [#add-on-selector]

Checkbox list of eligible add-ons with images and pricing. Fetches from the API based on the current activity, date, time, and equipment selection.

```javascript
document.addEventListener('timeslot-select', async (e) => {
  const addons = document.querySelector('resytech-addon-selector');
  addons.activityId = selectedActivity.uuid;
  addons.date = selectedDate;
  addons.time = '10:00';
  addons.durationId = selectedDuration.uuid;
  addons.equipment = [{ equipmentUuid: selectedEquipment.uuid, quantity: 1, seats: 1 }];
  await addons.load();
});
```

Attributes [#attributes-3]

| Attribute       | Type      | Default   | Description                    |
| --------------- | --------- | --------- | ------------------------------ |
| `activity-id`   | `string`  | —         | Activity UUID                  |
| `date`          | `string`  | —         | Booking date                   |
| `time`          | `string`  | —         | Booking time (HH:MM)           |
| `duration-id`   | `string`  | —         | Duration UUID                  |
| `duration-mins` | `number`  | —         | Duration minutes (alternative) |
| `show-label`    | `boolean` | `true`    | Show heading label             |
| `label-text`    | `string`  | `Add-ons` | Label text                     |

Events [#events-3]

| Event          | Detail                          |
| -------------- | ------------------------------- |
| `addon-change` | `{ selected: EligibleAddon[] }` |

Reading Selections [#reading-selections]

```javascript
const addons = document.querySelector('resytech-addon-selector');
const selected = addons.selectedAddons; // EligibleAddon[]
```

The component renders nothing (no empty state) when no add-ons are eligible — it simply disappears from the page.

***

Cart Summary [#cart-summary]

Displays a price breakdown from a server shopping cart response. This is a display-only component — set the `cart` property from a cart API response.

```javascript
const response = await api.cart.createOrUpdateCart({ cart: myCart });
if (response.success) {
  const summary = document.querySelector('resytech-cart-summary');
  summary.cart = response.cart;
}
```

Attributes [#attributes-4]

| Attribute         | Type      | Default         | Description                        |
| ----------------- | --------- | --------------- | ---------------------------------- |
| `show-title`      | `boolean` | `true`          | Show "Order Summary" heading       |
| `title-text`      | `string`  | `Order Summary` | Heading text                       |
| `show-line-items` | `boolean` | `true`          | Show individual line items         |
| `show-fees`       | `boolean` | `true`          | Show taxes & fees row (expandable) |

Display [#display]

The summary shows:

* Line items (equipment, add-ons)
* Subtotal
* Discount with coupon code badge (if applied)
* Trip protection (if selected)
* Taxes & fees with expandable detail
* Gift card applied with code badge (if applied)
* **Total**
* Due now / due later split (for deposit bookings)
