Prototype logic

Staff-friendly summary of the rules the prototype enforces. Items marked
Skiidy to confirm
need a final decision before production.

Booking Flow — Logic & UX Update

Updated 22 August 2026

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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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

Updated 31 July 2026

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. 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.60
  2. 2. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 21. Free equipment

    • Standard luggage, skis, snowboards and wheelchair / mobility equipment are free and must not affect the booking total.
  22. 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. 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. 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. 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 confirm
    Confirm 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 confirm
    Confirm 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 confirm
    Choose 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 confirm
    Confirm 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 confirm
    Confirm 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 confirm
    Confirm 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.

Prototype feedback (0)

No feedback yet — use the button on any page.