Skip to content

Add-on picker

Status: ✅ Implemented

Sell small extras — parking, late check-out, an all-inclusive package — as a grid of toggleable cards where the operator (or the customer) sees the running total update live, instead of hunting prices through a form.

Use the AddOnPicker component: a totalLabel and currency for the header-right running total, an optional actionId, and a list of AddOn cards. Each card has an icon chip, a title, a muted description, and either a price line (price, optionally / unit) with a +/ toggle, or an includedLabel (e.g. a loyalty perk) shown instead of a price and with no toggle. added(true) pre-selects a card.

@Section("Extras")
Component extras = AddOnPicker.builder()
.totalLabel("Añadidos").currency("").actionId("extrasChanged")
.items(List.of(
AddOn.builder().id("allinc").icon("🍹").title("Paquete All Inclusive")
.description("Todo incluido · 7 noches · 2 pax")
.price(343.0).unit("estancia").build(),
AddOn.builder().id("parking").icon("🅿").title("Parking")
.description("Cubierto · Vigilancia 24h")
.includedLabel("Incluido Platinum").build(),
AddOn.builder().id("late").icon("🕕").title("Late check-out")
.description("Hasta las 18:00h")
.price(40.0).added(true).build()))
.build();

Add-on picker

Toggling happens client-side (the total recomputes live as the sum of added prices, formatted like the money stereotype). On every toggle, when actionId is present, the component dispatches the standard action-requested event with parameters { "_item": id, "_added": bool, "_total": number }, so the server can persist each change incrementally.

Use an AddOnPicker for multiple optional priced items attached to a booking/order, where the running total is the feedback that drives the sale. For one big contextual upsell use an Offer card; to compare plans use a Pricing table; the resulting charges land in a Ledger at check-out. See it composed into a whole screen in Front-office screens. Demo: /addon-picker-demo.