Prototype logic
Booking Flow — Logic & UX Update
Latest source of truth. This update takes precedence wherever it conflicts with earlier rules on this page. It covers per-direction passenger counts, train-time handling, progressive-collapse behaviour, booking-summary navigation and the booking-stage progress indicator. Production developers should reproduce the described behaviour — not the prototype-only styling or values literally.
1. Per-direction passenger counts
- • The overall booking party size remains a group-level value, but each travel direction now has its own traveller count.
- • Each direction defaults to the overall booking party size, so normal return bookings are unchanged unless the customer needs an asymmetric booking.
- • Changing a direction traveller count affects only that direction and must never alter the overall booking party size.
Overall booking party: 2 passengers Geneva → Morzine Passengers travelling this direction: 2 Morzine → Geneva Passengers travelling this direction: 1
2. Direction allocation validation
- • Allocation is validated against the number travelling in that direction: allocated passengers in direction = passengers travelling in direction.
- • Do not require every direction to equal the overall booking party size.
Overall party = 2 Arrival traveller count 2 · allocated 2 → complete Departure traveller count 1 · allocated 1 → complete Valid booking — customer may continue.
3. Split journeys use the direction traveller count
- • Where a direction is split, the sum of journey passenger counts must equal that direction's traveller count.
- • If the traveller count is reduced below the number already allocated: do not silently change journey passenger counts, do not remove journeys, show a clear validation warning and require the customer to correct the affected journey allocation.
Overall party = 4 Arrival traveller count 4: Journey 1 = 2, Journey 2 = 2 → 4 of 4 allocated Departure traveller count 3: Journey 3 = 1, Journey 4 = 2 → 3 of 3 allocated
4. Train-station time input
- • Train-station journeys use the compact HH : MM selector — generically for any train station (Geneva Airport Train Station, Geneva Cornavin, future stations).
- • Hours 00–23, minutes 00–59. Train times are not restricted to 5-minute increments.
- • This rule does not apply to flight-time handling.
5. Train time commits only when complete
- • The picker behaves as one time input. Selecting the hour must not complete or collapse the subsection, scroll the page, move focus, or advance the booking.
- • The value commits only once both HH and MM are selected. Saved format remains HH:mm.
6. Progressive-collapse behaviour
- • Completed information reduces in visual prominence as the customer progresses.
- • Journeys stage — completed subsections collapse into concise confirmation rows with Edit: flight / train details, accommodation, transfer service.
- • Details stage — same treatment for journey contact, child seats, luggage / equipment.
- • Editing a completed subsection reopens the full controls with existing values preserved. Validation errors or changes that invalidate a subsection reopen it automatically.
7. Completed journey collapse
- • Once a whole journey is validly complete it collapses into a compact journey summary retaining reassurance detail: route, date, passengers, flight / train timing, transfer service, price, accommodation and completion state.
- • The customer reopens it with Edit.
8. Active journey hierarchy
- • Only the journey being worked on is visually dominant: current journey expanded / active, completed journey compact / quieter, pending journey visible but secondary.
9. Booking summary progress
- • The right-hand 'Your booking' summary is both a summary and a lightweight progress aid, showing 'Journey details complete' or 'Journey details incomplete' per journey.
- • Incomplete statuses are actionable: clicking takes the customer to the relevant journey and opens/focuses the first missing area where practical. It must not become a full checklist.
10. Active journey in booking summary
- • The journey currently being edited gets a subtle active / Editing state in the summary. This is an orientation aid only and must not compete with the main journey form.
11. Booking-details progress
- • On the Details stage the summary also shows a booking-level status: 'Passenger details incomplete' then 'Passenger details complete'.
- • An incomplete status navigates to the first missing required Details field or section.
12. Actionable validation alerts
- • Attention / validation messages help the customer fix the issue rather than acting as passive text where a clear navigation target exists.
- • Passenger allocation needs attention → jump to the affected direction. Travel timing still needed → open the affected journey / timing section. Passenger details incomplete → jump to the first missing Details field.
13. Booking-stage progress indicator
- • Three stages: Your journeys, Details, Review & pay. Desktop uses full wording; mobile uses Journeys / Details / Review.
- • Completed stage = green check, green label, green connecting line. Current stage = solid Skiidy navy with white text. Future stage = pale grey, muted text, grey connecting line.
- • Payment remains part of Stage 3 and does not create a fourth booking stage.
14. Stepper is non-sticky
- • The booking progress stepper stays non-sticky: the site header is already sticky and focus / scroll offsets are tuned around it. Do not add a second sticky progress bar unless the production implementation revisits the scroll-offset architecture.
Booking Flow — Pricing, Journey Structure & Extras Logic Update
Written for Chris / production developers. Reproduce the behavioural rules and data flow described here — do not copy prototype-only pricing values literally. Where earlier sections on this page conflict with this update (Geneva location handling, early pricing, per-journey Geneva endpoint changes, promo behaviour, child-seat pricing, equipment pricing), this update takes precedence.
1. Stage 1 — Search + indicative pricing
- • Stage 1 collects the minimum needed to establish meaningful indicative pricing and product eligibility: From, To, Single/Return, date(s), passenger count, optional promo code.
- • Immediately after search, show indicative Shared / Private pricing. Final price and availability are only confirmed once enough timing information exists.
Shared transfer From €156 Private transfer From €312 With a promo applied: Shared transfer From €156 Promo from €132.602. Geneva location types are first-class pricing / availability inputs
- • Never treat every Geneva journey as 'Geneva Airport'. Stage 1 distinguishes: Geneva Airport, Geneva Airport Train Station, Geneva Cornavin Train Station, Geneva Hotel.
- • Location type can affect shared availability, private availability, indicative price, final price and the required travel-detail inputs.
- • Current prototype behaviour — Geneva Airport: shared available (subject to rules/timing), private available. Geneva Airport Train Station: shared available (subject to rules/timing), private available. Geneva Cornavin: shared unavailable, private available. Geneva Hotel: shared unavailable, private available.
- • The Cornavin / Geneva Hotel private uplift in the prototype is a prototype assumption only — NOT confirmed production pricing.
3. Stage 1 location TYPE vs later operational detail
- • Stage 1 identifies the Geneva location type. Later stages collect the specific operational detail.
- • Geneva Hotel → actual hotel/property, address, pickup/drop-off time. Geneva Airport Train Station / Cornavin → train timing (station fixed by the journey location). Geneva Airport → flight, arrival/departure timing, terminal.
- • Do not ask the customer to reselect the Geneva location type unless they deliberately change the journey endpoint.
4. Stage 2 uses a FROM → TO journey structure
- • Each journey card follows the actual direction of travel and reads naturally in travel order. Keep the same model in Review where practical.
Geneva Airport → Morzine Morzine → Geneva Airport FROM Geneva Airport FROM Resort accommodation [flight / travel info] TO Geneva Airport TO Resort accommodation [flight / travel info] [address]5. Travel information belongs to the relevant endpoint
- • The endpoint determines which operational input appears — Geneva Airport: flight search, flight timing, terminal. Airport Train Station / Cornavin: station fixed from the journey location + timing. Geneva Hotel: specific hotel/address + pickup/drop-off time. Resort: accommodation/property + address.
- • Avoid duplicate location selection anywhere in the flow.
6. Journey-specific Geneva endpoint overrides
- • Stage 1 sets the default Geneva endpoint; each journey can override it independently. Available per-journey types: Geneva Airport, Geneva Airport Train Station, Geneva Cornavin, Geneva Hotel.
- • This must also work for split bookings.
Stage 1: Geneva Hotel ↔ Morzine Journey 1 Geneva Hotel → Morzine (unchanged) Journey 2 Morzine → Geneva Hotel → Morzine → Geneva Airport
7. Endpoint override behaviour
- • Changing one journey's Geneva endpoint immediately updates FROM/TO, the route/direction label, sidebar, Review, confirmation, manage-booking and operational output.
- • Swap the dependent controls: Geneva Hotel → Geneva Airport removes hotel/address and hotel timing and shows airport/flight controls.
- • Changing one journey must never change another journey.
8. Clear incompatible dependent data safely
- • On endpoint change, clear ONLY state that is no longer valid. Airport → Hotel: clear flight/terminal data, clear the selected service if no longer eligible, clear the stale confirmed price, clear any stale special-availability offer, fall back to indicative pricing, and require hotel/address/time.
- • Never clear unrelated data: passengers, resort accommodation, journey date, or other journeys' overrides.
9. Direction grouping rule
- • Group journeys by travel direction + resort zone, NOT by the exact Geneva endpoint. Grouping by endpoint makes an overridden journey drop out of its direction group and breaks passenger allocation.
10. Indicative → confirmed pricing
- • Before sufficient travel timing exists show 'INDICATIVE PRICES'; once enough timing exists show 'CONFIRMED OPTIONS'.
- • Show the confirmed price as soon as timing allows — do not wait for contact details, child seats or equipment.
- • If timing becomes invalid or the endpoint changes, recalculate or revert to indicative pricing.
11. Ops special availability pricing
- • Ops can release special availability prices for specific timing/capacity situations. They appear only once sufficient timing exists, and are alternative bookable offers — not discounts applied on top of standard pricing.
Standard Shared €156 Special availability €100 Fixed departure 15:00 Expected additional wait 15 minutes
12. Promo code placement
- • Promo code is available from Stage 1 via a low-prominence but discoverable control ('Have a promo code?'), expanding to a labelled input plus Apply.
- • Applied state reads e.g. 'INDIGI · 15% off applied' with Change / Remove, and stays visible through the flow.
13. Promo discount rules
- • Promo codes apply ONLY to eligible standard transfer fares. They must not apply to Ops special-availability prices, bikes, oversized items or any other chargeable extra. No stacking.
Standard transfer price €390.00 15% promo discount −€58.50 Chargeable extras +€34.00 Final total €365.50
14. Promo vs Ops special
- • If both exist, present them as separate alternatives; the Ops special is never reduced by the promo.
- • If the promo-adjusted standard price is cheaper than the Ops special, do not label the Ops special as the best deal simply because it is a special.
Standard Shared €156 Promo standard price €132.60 Ops special €100 (stays €100)
15. Shared-unavailable messaging
- • Never show misleading shared indicative pricing where shared is unavailable.
- • Geneva Hotel: “Shared transfers aren’t available from Geneva hotels. Private door-to-door transfers are available.”
- • Geneva Cornavin: “Shared transfers aren’t available from Geneva Cornavin. Private transfers are available from this location.”
- • Terminal 1 / Terminal 2 messaging is for the airport scenario only.
16. Terminal 2 behaviour
- • Terminal detail stays part of the flight workflow. Shared passengers arriving/departing Terminal 2 are told the shared meeting/drop-off point is Terminal 1 and they make their own way between terminals.
- • Do not reuse this message for Cornavin or Geneva Hotel.
17. Accommodation inheritance unchanged
- • Moving accommodation into the FROM / TO structure must not change propagation, per-journey override, 'same accommodation for this booking', Change / Remove, or split-journey behaviour.
18. Stage 2 entry scroll behaviour
- • On first entry via 'Continue to journeys', Stage 2 starts at the top with Journey 1 in view — never auto-scrolled to Journey 2 or another incomplete journey.
- • The prototype bug was accommodation search focusing on mount/remount. Focus should occur only on a genuine edit transition and must not move the viewport unexpectedly. Later intentional auto-advance remains.
19. Child seats are free of charge
- • All child-seat types (Maxi-Cosi / infant carrier, Baby seat, Booster) are free. The UI states 'CHILD SEATS — FREE OF CHARGE'. Adding or removing seats must never affect the total.
- • Keep the existing default + per-journey inheritance behaviour.
20. Quantity 0 is a valid state
- • 0 → 1 → 0 must work normally. An explicit 0 must not be read as missing state, a reset, removal of an override, or a return to the booking default.
- • Avoid truthiness logic where 0 is interpreted as false/missing.
21. Free equipment
- • Standard luggage, skis, snowboards and wheelchair / mobility equipment are free and must not affect the booking total.
22. Chargeable equipment
- • Only bikes (€12 each per journey) and oversized items (€10 each per journey) currently carry a charge, and are clearly marked chargeable.
- • Treat these values as prototype prices unless confirmed separately for production.
23. Chargeable extras in Your Booking
- • Paid extras are itemised in the Your Booking sidebar and in Review. Free extras must not create €0 price lines in the monetary summary, though they stay visible in journey details.
Standard transfer price €468 Bike x1 · Journey 1 €12 Oversized item x1 · Journey 1 €10 Bike x1 · Journey 2 €12 Oversized item x1 · Journey 2 €10 TOTAL €512
24. Pricing calculation order
- • eligible standard transfer fares − promo discount + chargeable extras = booking total.
- • Ops-special journeys are priced separately and are never promo discounted.
25. High-level production implementation model
- • 1. Search-level defaults — route / Geneva location type, date, passengers, promo.
- • 2. Journey-level configuration — actual FROM → TO, passenger allocation, per-journey Geneva endpoint override.
- • 3. Operational timing — flight, train, Geneva hotel pickup/drop-off time.
- • 4. Pricing / availability — indicative first, confirmed as soon as enough timing exists.
- • 5. Booking details — contacts, child seats, equipment.
- • 6. Review / payment — transparent transfer price, discounts, chargeable extras, final total.
Booking structure
- • One GroupBooking contains one or more independent JourneyLeg records.
- • Each JourneyLeg has its own reference (SG-DEMOxx-N), its own service, its own contact, its own price.
- • Party leader / payer is stored once at the group level.
Single vs Return
- • Return generates two journeys: outbound and its reversed direction.
- • Route direction is stored literally (from → to). The model does not assume Geneva is always first.
- • One-way generates one journey; extra journeys can still be added.
Multiple journeys
- • Any direction can be split into multiple journeys with different dates, passengers and services.
- • One direction can remain together while the other is split.
- • SUPERSEDED 22 Aug 2026: 'A direction is complete only when allocated passengers = total booking passengers.' Now: a direction is complete only when allocated passengers = passengers travelling in that direction. The overall booking party size stays global, but each direction can have a different traveller count.
- • Continue is blocked while allocation is invalid.
Passenger allocation
- • Total group passengers is set at search.
- • Prototype allows 1–20 passengers per booking.
- • No individual passenger name list is collected.
- • 22 Aug 2026: each direction defaults to the total group passenger count but can be independently reduced; allocation validation uses the direction traveller count.
Shared vs Private
- • Shared and Private can be mixed across journeys within one booking.
- • Shared uses per-passenger pricing. Private uses per-passenger pricing with a minimum.
- • Deterministic prototype pricing used throughout — not production.
- • SUPERSEDED 31 Jul 2026: shared availability is no longer universal — it depends on the Geneva location type. See the 31 July 2026 update above.
- Skiidy to confirmConfirm production pricing model with Skiidy.
Terminal rules
- • Shared + T1 (arrival/departure) — normal instructions.
- • Shared + T2 arrival — customer may store T2 flight; operational meeting point remains T1; explicit warning shown; acknowledgement required.
- • Shared + T2 departure — operational drop-off remains T1; explicit warning shown; acknowledgement required.
- • Private + T1/T2 — direct pickup/drop-off at the chosen terminal.
Plane / train / hotel rules
- • Geneva-side connection selected per journey with method-appropriate fields.
- • SUPERSEDED 22 Aug 2026: 'Shared + train/hotel — meeting point remains Terminal 1; customer routes themselves.' Now: Geneva Airport Train Station is a first-class endpoint and may support Shared / Private per pricing and availability rules; Geneva Cornavin and Geneva hotels are Private-only in the prototype; train-station journeys use station-specific train timing (any minute 00–59, no 5-minute restriction); Terminal 1 logic applies to airport-terminal scenarios only.
- • Private + train/hotel — direct collection/drop at approved locations (Cornavin, GVA rail, approved Geneva hotels).
- • Private + Geneva hotel — required time: collectionTime (resort-bound) or requestedArrivalTime (Geneva-bound).
- • Private + train station — required time: arrivalTime (resort-bound, when train reaches Geneva) or departureTime (Geneva-bound, when the customer must catch the train).
- • Journey titles are derived via getJourneyDisplayRoute(journey): shared journeys always show 'Geneva Airport Terminal 1'; private hotel/train journeys show the specific hotel or station name once selected.
- Skiidy to confirmConfirm approved Geneva hotel and train station whitelist.
Party leader vs journey contact
- • Party leader collected once at group level. Payer and primary contact.
- • Each journey confirms whether the party leader is travelling; if not, a per-journey travelling contact is collected.
Flight search
- • Prototype searches an in-memory dataset (~55 flight templates across 16 airlines) materialized against the selected journey date.
- • Results are filtered by date, direction (Geneva arrivals for to-resort; Geneva departures for from-resort), and query (min. 2 chars, debounced).
- • Priority ranking: exact flight-number match, then flight-number prefix, airline code, airline name prefix, airport/city match, then general substring.
- • Case- and accent-insensitive. Max 5 initial results with 'View more'.
- • Manual entry supported; flagged as FLIGHT_MANUAL for staff verification.
- • Production will call a protected Skiidy backend endpoint connected to an external scheduled-flight and flight-status provider.
- Skiidy to confirmChoose a real flight-data API (e.g. FlightAware, AviationStack) for production.
Accommodation & Geneva-side search
- • Resort accommodation search uses an in-memory dataset of ~45 fictional properties across Morzine, Les Gets, Avoriaz, Montriond, St Jean d'Aulps and Prodains.
- • Results are filtered to the selected journey's resort/service zone (Prodains records live under Morzine).
- • Geneva-side hotels and stations are separate datasets — hotel results only appear when 'Geneva hotel' is selected on a private journey; station results only when 'Train station' is selected.
- • All searches: 2+ chars, debounced, case- and accent-insensitive, max 5 initial results with 'View more', manual-entry / not-listed fallback.
- • Selected results show as a compact confirmation card with Change / Remove actions.
- Skiidy to confirmConfirm production address-lookup provider (e.g. Google Places, HERE) and whitelist rules.
Child seat logic
- • Categories: Maxi-Cosi/infant, Baby seat, Booster.
- • SUPERSEDED 31 Jul 2026: all child-seat types are FREE OF CHARGE and never affect the total.
- • Draft age/height/weight guidance shown — not a legal certification.
- • Prototype warns if selected seats exceed passengers on that journey.
- • Default + per-journey override; an explicit quantity of 0 is a valid value, not 'unset'.
- Skiidy to confirmConfirm final child-seat category names and guidance text.
Equipment logic
- • Standard luggage, skis, snowboards, bikes, wheelchair/mobility, oversized, other.
- • SUPERSEDED 31 Jul 2026: luggage, skis, snowboards and wheelchair/mobility are free. Only bikes (€12 each per journey) and oversized items (€10 each per journey) are chargeable — prototype values.
- • Special requirements captured but not visually dominant.
- • Same-for-all default; per-journey overrides allowed.
- Skiidy to confirmConfirm production surcharges for bikes / oversized / wheelchair-adapted vehicles.
Review flags
- • SHARED_T2_ARRIVAL / SHARED_T2_DEPARTURE — operational T1 override.
- • FLIGHT_MANUAL — needs staff verification.
- • SHARED_NON_TERMINAL — shared with train/hotel context.
Simulated functionality
- • No real payment provider.
- • No real email delivery.
- • No live flight API — uses local dataset.
- • No Supabase, no login, no live availability.
- • Booking persisted in browser localStorage.
Future production integrations
- • Payment provider (Stripe or existing Skiidy PSP).
- • Transactional email (booking confirmation, driver contact 48h out).
- • Live flight API replacing FLIGHTS[].
- • Real availability rules (blackout dates, vehicle capacity).
- • CRM / dispatch system integration for GroupBooking + JourneyLeg records.
