Skip to content

feat: Add bike ticket purchase flow#5570

Merged
cbrevik merged 35 commits intomasterfrom
strandlie/bike-ticket
Nov 27, 2025
Merged

feat: Add bike ticket purchase flow#5570
cbrevik merged 35 commits intomasterfrom
strandlie/bike-ticket

Conversation

@strandlie
Copy link
Copy Markdown
Contributor

@strandlie strandlie commented Nov 7, 2025

Closes https://github.com/AtB-AS/kundevendt/issues/4282

Adds a complete bike ticket purchase flow where you can select a bike on an equal footing with user profiles in the bottom sheet for travellers. Also introduces support for "baggage products" as a specific type of supplement product.

Changes

  • Support baggage products with counts and offer on an equal footing with user profiles in use-offer-state.ts.
  • Rewrite how to calculate the price total in use-offer-state.ts.
  • ⚠️ Add own subtype BaggageTicketOffer Zod-schema, based on the TicketOffer-schema. This new schema assumes that there is only one supplement product in the supplementProducts-array, and the fareProduct (ref) is null/undefined, if it is a baggage-type offer. This is something we will have to change in the future when we want to support more complex offers. Backend are aware of this, and will ensure that this assumption/requirement is held until we decide to rewrite the ticket data structure.
  • Introduced a new BaggageProduct type (a subtype of SupplementProduct with specific properties) and corresponding runtime validation using zod in src/modules/configuration/types.ts. Use this type to safeParse baggage products in getBaggageProducts and use-offer-state and friends.
  • Added baggageType on SupplementProduct which is used in the OfferSearch-request.
  • Add supplementProducts on TicketOffer.
  • Make fareProduct on TicketOffer nullable (it will always be null on baggage-type offers).

Test changes

  • Added comprehensive tests to ensure correct handling of baggage products in purchase selections, including validation against product limitations and allowing the bicycle ticket to be selected by itself.
  • Had to replace .toBe() with .toStrictEqual() for deep object comparison in several tests.

Dependent on:

@strandlie strandlie marked this pull request as draft November 7, 2025 07:18
@strandlie strandlie force-pushed the strandlie/bike-ticket branch from 465ab41 to d30ef5e Compare November 7, 2025 07:57
@cbrevik cbrevik force-pushed the strandlie/bike-ticket branch from 652c622 to cfa79b6 Compare November 21, 2025 12:31
@cbrevik cbrevik changed the title Strandlie/bike ticket feat: Add bike purchase flow Nov 21, 2025
@cbrevik cbrevik changed the title feat: Add bike purchase flow feat: Add bike ticket purchase flow Nov 21, 2025
@cbrevik cbrevik marked this pull request as ready for review November 24, 2025 11:13
@cbrevik
Copy link
Copy Markdown
Contributor

cbrevik commented Nov 25, 2025

Triggered a build yesterday, so build 1763997892 is testable with bike ticket purchase. Cc @tormoseng et al

@tormoseng
Copy link
Copy Markdown
Member

Triggered a build yesterday, so build 1763997892 is testable with bike ticket purchase. Cc @tormoseng et al

Seems to be working fine! But, as we talked about @cbrevik , only the 1 adult part of the single ticket is shown under the "Repeat purchase" section in the app. And the single bike doesn't show at all. Maybe we need to talk to @Sebstorvik about how we like this to be presented (do we want bike tickets to appear or not).

Comment thread src/components/sections/items/CounterSectionItem.tsx Outdated
Comment thread src/modules/configuration/types.ts Outdated
Comment on lines +109 to +113
buildState.userProfilesWithCount = userProfilesWithCount;
return builder;
},
baggageProducts: (baggageProductsWithCount) => {
buildState.baggageProductsWithCount = baggageProductsWithCount;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we adding the state to buildState instead of using selection here? I also don't quite understand why so much logic has been put into the build function 🤔

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a bit complex, and may be easier to explain in person. But the problem is that if we don't do this, the building ends up in an inconsistent state. And I'd argue you would want to put even more logic in the build function for this builder to not have bugs.

What we are solving here specifically is the business rule that you have to have either one or more useprofiles, or one or more baggages. If you set them separately, it is difficult to validate that business rule.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are more things that needs to be fixed in the builder, but this was sort of a minimum change that at least does not introduce more inconsistent state 😅

Copy link
Copy Markdown
Member

@rosvik rosvik Nov 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Had a talk with @strandlie, and explained why this was confusing to me. I'm wondering if we can avoid the buildState concept, and move some logic from the build step back here.

Håkon suggested we all could have a talk tomorrow? Might be easier to talk about in person indeed 😄

Comment thread src/modules/purchase-selection/types.ts Outdated
Comment thread src/stacks-hierarchy/Root_PurchaseOverviewScreen/use-offer-state.ts
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The pricing calculations are getting hard to reason about, and it's important to get correct. Is it time to extract it out of use-offer-state and add some tests?

Comment thread src/modules/purchase-selection/use-selectable-baggage-products.ts
Copy link
Copy Markdown
Member

@rosvik rosvik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good work 🙌 Talked with @cbrevik about my remaining comments, and it makes sense to handle those as separate PRs:

  1. Extract pricing calculations from use-offers-state, and add tests.
  2. Update the purchase selection builder, and maybe handle invalid states better.

Comment thread src/stacks-hierarchy/Root_PurchaseOverviewScreen/use-offer-state.ts
…te.ts

Co-authored-by: Johannes Røsvik <j.rosvik@gmail.com>
@cbrevik cbrevik merged commit 72e47ef into master Nov 27, 2025
7 checks passed
@cbrevik cbrevik deleted the strandlie/bike-ticket branch November 27, 2025 13:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants