ProjectR Docs
Screen MapMobile app

Bag & checkout

Mobile app: every screen, state and flow in Bag & checkout.

9 screens and overlays, 76 states, 3 flows. Frames show a real screenshot where one was captured and a wireframe card where the state needs data or conditions a device run cannot produce.

Flows

Bag to Shopify checkout

Step 1
My Bag (iOS)My Bag (Android)
Bag lists lines, voucher pill and a sticky Checkout bar
Tap Checkout
Step 2
Wireframe

Shopify checkout

Success

Completed with order id

De-duplicated, session client_completed, bag replaces to confirmation

Needs data: Capture audit 2026-10-30. ios: Real checkout/payment required; do not bypass route allowlist. android: Real checkout/payment required; do not bypass route allowlist.

Shopify checkout sheet (hosted page) completes with an order
Redirect
Step 3
Wireframe

Order Confirmed!

Success

Order Confirmed!

Order document exists, even if payment status is pending

Needs data: Capture audit 2026-10-30. ios: Real checkout/payment required; do not bypass route allowlist. android: Real checkout/payment required; do not bypass route allowlist.

Order confirmation replaces the bag

Branches and edge cases

Emptyfrom step 1
My Bag, Your bag is empty (iOS)My Bag, Your bag is empty (Android)
Bag is empty
Gatefrom step 1
Sign in, Guest sees reason copy (iOS)Sign in, Guest sees reason copy (Android)
Guest taps Checkout
Gatefrom step 1
My Bag, Locked checkout session (iOS)My Bag, Locked checkout session (Android)
Checkout session already locked
Edgefrom step 1
Wireframe

My Bag

Loading

Syncing bag

Pending writes disable the sticky button reading Syncing bag…

Not reachable on a device: Needs a network fault, crash, race or code change to show

Cart still syncing: button disabled
Edgefrom step 1
My Bag, Out of stock line (iOS)My Bag, Out of stock line (Android)
A line is out of stock
Errorfrom step 1
Wireframe

Items Unavailable

Edge

Item count subtitle

1 item or N items no longer available for checkout

Needs data: Capture audit 2026-10-30. ios: Requires Shopify to reject a line Convex still reports in stock. Current sold-out lines disable Checkout before this modal; cannot stage through current dev cart without a store-side mismatch. android: Requires Shopify to reject a line Convex still reports in stock. Current sold-out lines disable Checkout before this modal; cannot stage through current dev cart without a store-side mismatch.

Prepare finds stock failure
Errorfrom step 1
Wireframe

My Bag

Error

Checkout Error toast

Prepare failed; raw message, or Too many requests when rate limited

Not reachable on a device: Needs a network fault, crash, race or code change to show

Network failure or rate limit
Edgefrom step 2
Shopify checkout, Closed without completing (iOS)Shopify checkout, Closed without completing (Android)
Sheet closed without completing
Edgefrom step 2
Wireframe

Shopify checkout

Edge

Completed without order details

No navigation; bag stays locked: Your order is being confirmed

Not reachable on a device: Needs a webhook-timing race after payment

Completed without order details
Edgefrom step 2
My Bag, Checkout Closed (iOS)My Bag, Checkout Closed (Android)
Bag locked until webhook
Flow diagram

Address selection

Step 1
My Bag (iOS)My Bag (Android)
Address line sits under the bag title
Tap the address line
Step 2
Deliver to, Default auto selected (iOS)Deliver to, Default auto selected (Android)
Deliver to sheet lists saved addresses
Select an address
Step 3
My Bag, Shipping estimate line (iOS)My Bag, Shipping estimate line (Android)
Shipping estimate refreshes for the address

Branches and edge cases

Gatefrom step 1
My Bag, Signed out bag (iOS)My Bag, Signed out bag (Android)
Guest has no picker
Edgefrom step 2
Wireframe

Deliver to

Loading

Loading addresses

Address list loading

Not reachable on a device: Needs a network fault, crash, race or code change to show

Addresses loading
Emptyfrom step 2
Deliver to, No saved addresses yet (iOS)Deliver to, No saved addresses yet (Android)
No saved addresses
Successfrom step 2
New Address / Edit Address (iOS)New Address / Edit Address (Android)
Add new address
Edgefrom step 2
Wireframe

New Address / Edit Address

Loading

Saving…

Save Address button shows Saving…

Not reachable on a device: Needs a network fault, crash, race or code change to show

Save in the form
Errorfrom step 2
Wireframe

New Address / Edit Address

Error

Unable to save address

Server message or Please try again; sheet stays open

Not reachable on a device: Needs a network fault, crash, race or code change to show

Save fails, form stays open
Flow diagram

Voucher and promo

Step 1
My Bag, Social proof and voucher hint (iOS)My Bag, Social proof and voucher hint (Android)
Bag shows a voucher pill for a signed-in user
Tap the voucher pill
Step 2
Vouchers & Promo, Recommended and expiry tags (iOS)Vouchers & Promo, Recommended and expiry tags (Android)
Vouchers and Promo sheet lists vouchers with tags
Type a promo code, Apply
Step 3
Wireframe

Vouchers & Promo

Success

Promo code applied

Valid promo; discount in total, row with remove control

Needs data: Capture audit 2026-10-30. ios: Needs valid live Shopify promo; no store writes or customer code use. android: Needs valid live Shopify promo; no store writes or customer code use.

Valid promo is applied
Close
Step 4
My Bag (iOS)My Bag (Android)
Discount shows in the bag total

Branches and edge cases

Emptyfrom step 1
Wireframe

Vouchers & Promo

Empty

No vouchers yet

Earn them through stamps and rewards

Needs data: Capture audit 2026-10-30. ios: All four fixed-OTP test accounts have vouchers. An isolated zero-voucher signed-in fixture is needed; existing rewards were not removed. android: All four fixed-OTP test accounts have vouchers. An isolated zero-voucher signed-in fixture is needed; existing rewards were not removed.

No usable vouchers: pill hidden
Successfrom step 1
Vouchers & Promo, Auto apply best voucher (iOS)Vouchers & Promo, Auto apply best voucher (Android)
Nothing applied: best voucher auto applied
Edgefrom step 2
Wireframe

Vouchers & Promo

Loading

Loading vouchers

Voucher list loading

Not reachable on a device: Needs a network fault, crash, race or code change to show

Vouchers loading
Errorfrom step 2
Wireframe

Vouchers & Promo

Error

Invalid promo code

Code failed validation

Needs data: Capture audit 2026-10-30. ios: Toast renders behind native sheet, already reproduced. android: Two attempts: invalid promo request finishes but no visible error toast appears over native voucher sheet. Rejected screenshot quarantined.

Invalid promo code
Errorfrom step 2
Wireframe

Vouchers & Promo

Error

Validate failed or throttled

Failed to validate, Sign in to apply, or Too many tries; code stays applied

Not reachable on a device: Needs a network fault, crash, race or code change to show

Validation fails or is throttled
Edgefrom step 3
Wireframe

Vouchers & Promo

Edge

Voucher or promo removed

Voucher unavailable or Minimum purchase of RMx not met after bag change

Needs data: Capture audit 2026-10-30. ios: Prior repeated runs failed to show intended toast; report bug instead of more capture retries. android: Prior repeated runs failed to show intended toast; report bug instead of more capture retries.

Voucher or promo removed
Flow diagram

Screens and states

My Bag

My Bag (iOS)My Bag (Android)
Screen(app)/(tabs)/my-bag

My Bag

Shopping bag with line editing, vouchers, address, estimate and checkout launcher

src/app/(app)/(tabs)/my-bag/index.tsx

Entry

  • Tab bar
  • PDP bag icon or View Bag
  • Checkout sign-in sheet return
  • Deep link

Exits

  • Checkout → Shopify checkout sheet
  • Checkout completed → order confirmation
  • Guest Checkout → sign-in sheet
  • Start shopping → All Products list
  • Voucher pill → VoucherSheet
  • Address line → AddressPickerModal

States · 22

Error
Whole tab crash · TabErrorBoundary for My Bagsrc/app/(app)/(tabs)/my-bag/index.tsx:902
Loading
No cart skeleton · Not empty while loading, so an empty list shows until data arrivessrc/features/cart/hooks/use-cart-page.ts:38
Empty
Your bag is empty · Start shopping button and You might also like railsrc/features/cart/components/empty-cart-state.tsx:37
Gate
Signed out bag · Local bag; no address, voucher pill or stamp note; West Malaysia shippingsrc/app/(app)/(tabs)/my-bag/index.tsx:714
Loading
Syncing bag · Pending writes disable the sticky button reading Syncing bag…src/features/cart/components/summary/sticky-checkout-bar.tsx:55
Edge
Guest merge dropped lines · Lines without product id dropped; toast N bag items couldn't syncsrc/features/cart/hooks/use-cart-auth-sync.ts:100
Error
Guest merge failed 3 times · Toast N cart items couldn't sync; Reopen your cart to retrysrc/features/cart/hooks/use-cart-auth-sync.ts:189
Edge
Out of stock line · Overlay, plus disabled, banner Some items are unavailable; checkout blocked until deselectedsrc/features/cart/components/minimal-cart-item.tsx:239
Edge
Quantity cap silent · Plus disables at inventory quantity with no message; unknown stock uncappedsrc/features/cart/hooks/use-server-cart.ts:88
Edge
Minus becomes trash · Quantity 1 minus removes the line; full swipe does not deletesrc/features/cart/components/minimal-cart-item.tsx:344
Error
Unable to Remove Item · Remove failed; check your connection toastsrc/app/(app)/(tabs)/my-bag/index.tsx:406
Edge
Silent price change · Live variant price updates total with no message; compare-at struck throughsrc/features/cart/hooks/use-server-cart.ts:47
Edge
Social proof and voucher hint · In N other bags at 2 or more; Eligible for voucher hint on scoped linessrc/features/cart/components/minimal-cart-item.tsx:306
Edge
Edit mode selection · Checkboxes after Edit; leaving Edit reselects all; totals use selectionsrc/app/(app)/(tabs)/my-bag/index.tsx:134
Edge
Shipping estimate line · Free RM100 West, RM150 East; flat RM10 West, from RM15 East; estimate onlysrc/features/cart/constants.ts:15
Edge
Stamp note · Signed in only; earns a stamp or Spend RMx more; hidden while loadingsrc/features/loyalty/components/stamps/bag-stamp-note.tsx:24
Gate
Locked checkout session · Checkout in progress card; edits show toast; Close checkout buttonsrc/app/(app)/(tabs)/my-bag/index.tsx:266
Edge
Resume checkout does not resume · Handler returns early with the lock toast when session is lockedsrc/app/(app)/(tabs)/my-bag/index.tsx:483
Success
Checkout Closed · Session closed; Your bag is ready to editsrc/app/(app)/(tabs)/my-bag/index.tsx:302
Error
Unable to Close Checkout · Close session failed; Please try againsrc/app/(app)/(tabs)/my-bag/index.tsx:302
Error
Checkout Error toast · Prepare failed; raw message, or Too many requests when rate limitedsrc/app/(app)/(tabs)/my-bag/index.tsx:509
Offline
Network retries then error · Preparation retries network errors twice (1 s, 2 s), then toast; no offline bannersrc/features/checkout/hooks/checkout-preparation.ts:466

Captured states

My Bag, Your bag is empty (iOS)My Bag, Your bag is empty (Android)
Your bag is empty
My Bag, Signed out bag (iOS)My Bag, Signed out bag (Android)
Signed out bag
My Bag, Out of stock line (iOS)My Bag, Out of stock line (Android)
Out of stock line
My Bag, Social proof and voucher hint (iOS)My Bag, Social proof and voucher hint (Android)
Social proof and voucher hint
My Bag, Shipping estimate line (iOS)My Bag, Shipping estimate line (Android)
Shipping estimate line
My Bag, Locked checkout session (iOS)My Bag, Locked checkout session (Android)
Locked checkout session
My Bag, Checkout Closed (iOS)My Bag, Checkout Closed (Android)
Checkout Closed

Vouchers & Promo

Wireframe

Vouchers & Promo

Needs data: Needs the QA account to hold a usable voucher

SheetVoucherSheet

Vouchers & Promo

Choose one voucher or enter one promo code for the order

src/features/cart/components/vouchers/voucher-sheet.tsx

Entry

  • Floating voucher pill on bag, signed in only

Exits

  • Tap coupon → applies or swaps
  • Apply promo → validates code
  • Swipe → close

States · 13

Loading
Loading vouchers · Voucher list loadingsrc/features/cart/components/vouchers/voucher-sheet.tsx:236
Edge
Almost there · Vouchers whose minimum spend is unmet; not tappablesrc/features/cart/components/vouchers/voucher-sheet.tsx:244
Empty
No vouchers yet · Earn them through stamps and rewardssrc/features/cart/components/vouchers/voucher-sheet.tsx:292
Edge
Recommended and expiry tags · Best code tagged; Applied, Expires today or in Nd, Min. spend, max RMx offsrc/features/cart/components/vouchers/voucher-sheet.tsx:84
Edge
Auto apply best voucher · Nothing applied: best voucher auto applied and rescored; deliberate pick keptsrc/features/cart/hooks/use-cart-discount.ts:209
Success
Promo code applied · Valid promo; discount in total, row with remove controlsrc/features/cart/hooks/use-cart-discount.ts:272
Error
Invalid promo code · Code failed validationsrc/features/cart/hooks/use-cart-discount.ts:272
Edge
Voucher or promo removed · Voucher unavailable or Minimum purchase of RMx not met after bag changesrc/features/cart/hooks/use-cart-discount.ts:405
Error
Validate failed or throttled · Failed to validate, Sign in to apply, or Too many tries; code stays appliedsrc/features/cart/hooks/use-cart-discount.ts:405
Edge
Unreachable with no vouchers · Pill hides with no vouchers, so promo code input cannot be reachedsrc/features/cart/components/vouchers/floating-voucher-button.tsx:46
Success
Your vouchers · Applicable vouchers with Apply, Applied and Recommended tagsrc/features/cart/components/vouchers/voucher-sheet.tsx
Empty
No vouchers · Empty image and copysrc/features/cart/components/vouchers/voucher-sheet.tsx
Edge
Skipped when cart empty · Query skipped when cartTotal <= 0; error hard-coded nullsrc/features/loyalty/hooks/use-applicable-vouchers.ts:42

Captured states

Vouchers & Promo, Recommended and expiry tags (iOS)Vouchers & Promo, Recommended and expiry tags (Android)
Recommended and expiry tags
Vouchers & Promo, Auto apply best voucher (iOS)Vouchers & Promo, Auto apply best voucher (Android)
Auto apply best voucher

Checkout details

Checkout details (iOS)Checkout details (Android)
SheetCheckoutDetailsSheet

Checkout details

Estimate breakdown with free shipping progress, stamp note and Checkout button

src/features/cart/components/summary/checkout-details-sheet.tsx

Entry

  • Tap total in sticky bar

Exits

  • Checkout → closes, starts checkout
  • Swipe → close

States · 2

Edge
Free shipping progress · RMx away from free shipping; hidden once shipping is freesrc/features/cart/components/summary/checkout-details-sheet.tsx:132
Edge
No error or empty state · Sheet and bar share one order estimate; final price may varysrc/app/(app)/(tabs)/my-bag/index.tsx:174

Choose variant

Wireframe

Choose variant

Not captured yet

SheetVariantSwitcherSheet

Choose variant

Change the variant of a bag line

src/features/cart/components/shared/variant-switcher-sheet.tsx

Entry

  • Variant pill on a bag line

Exits

  • Pick variant → updates line, closes

States · 5

Loading
Loading variants · Product detail still loadingsrc/features/cart/components/shared/variant-switcher-sheet.tsx:118
Empty
No other variants · One or zero real variantssrc/features/cart/components/shared/variant-switcher-sheet.tsx:122
Edge
Current and unavailable disabled · Current variant selected; unavailable ones show Out of stocksrc/features/cart/components/shared/variant-switcher-sheet.tsx:109
Success
Variant updated · Change saved; merges into existing line if variant already in bagpackages/backend/convex/cartSync/mutations.ts:421
Error
Unable to change variant · Server message, e.g. variant does not belong or is unavailablepackages/backend/convex/cartSync/mutations.ts:393

Deliver to

Deliver to (iOS)Deliver to (Android)
SheetAddressPickerModal

Deliver to

Choose delivery address for shipping estimate and checkout prefill

src/features/addresses/components/address-picker-modal.tsx

Entry

  • Address line under bag title, signed in

Exits

  • Choose address → sets and closes
  • Add new address → address form after 300 ms

States · 3

Loading
Loading addresses · Address list loadingsrc/features/addresses/components/address-picker-modal.tsx:66
Empty
No saved addresses yet · Add one to speed up checkout; bag line reads Add delivery addresssrc/features/addresses/components/address-picker-modal.tsx:70
Edge
Default auto selected · First address becomes default and selected; later ones saved but not selectedpackages/backend/convex/userAddresses/mutations.ts:129

Captured states

Deliver to, No saved addresses yet (iOS)Deliver to, No saved addresses yet (Android)
No saved addresses yet
Deliver to, Default auto selected (iOS)Deliver to, Default auto selected (Android)
Default auto selected

New Address / Edit Address

New Address / Edit Address (iOS)New Address / Edit Address (Android)
Sheet/(app)/address-form

New Address / Edit Address

Create or edit a saved address with search, locate and validation

src/app/(app)/address-form.tsx

Entry

  • Address picker Add new address
  • Profile address book edit

Exits

  • Save → back to previous screen
  • Swipe down → close

States · 12

Loading
Waiting for address list · Edit with id shows centred spinner until list resolvessrc/app/(app)/address-form.tsx:53
Error
Address not found · Deleted on another device; toast then dismisssrc/app/(app)/address-form.tsx:29
Gate
Signed out deep link · Toast Sign in to edit your saved addresses, then dismisssrc/app/(app)/address-form.tsx:58
Loading
Address search spinner · Suggestions after 3 chars with 350 ms debouncesrc/features/addresses/components/address-form.tsx:286
Gate
Location permission needed · Use current location denied; danger toastsrc/features/addresses/components/address-form.tsx:336
Error
Could not find your address · Reverse geocode found nothing; enter it manuallysrc/features/addresses/components/address-form.tsx:336
Error
Unable to get your location · Location lookup failedsrc/features/addresses/components/address-form.tsx:336
Error
Field validation errors · Name, phone 7 to 15 digits, line 1, city, state, postcode 5 digitssrc/features/addresses/validation/address-schema.ts:10
Edge
Postcode fills state · Postcode autofills state; phone gets calling code, leading zeros strippedsrc/features/addresses/utils/postcode-to-state.ts
Loading
Saving… · Save Address button shows Saving…src/features/addresses/components/address-form.tsx:421
Error
Unable to save address · Server message or Please try again; sheet stays opensrc/features/addresses/components/address-form.tsx:421
Empty
No states found · State picker search matches nothing; country picker titled Select countrysrc/features/addresses/components/address-form.tsx:708

Items Unavailable

Wireframe

Items Unavailable

Needs data: Needs a cart line that is out of stock at checkout

ModalUnavailableItemsModal

Items Unavailable

Lists bag lines the server refused at checkout, with reasons and stock counts

src/features/checkout/components/unavailable-items-modal.tsx

Entry

  • Inventory error from prepare checkout (bag only)

Exits

  • Remove & Continue Checkout → removes lines, back on bag
  • Return to Cart → close

States · 4

Edge
Item count subtitle · 1 item or N items no longer available for checkoutsrc/features/checkout/components/unavailable-items-modal.tsx:71
Edge
Reason chips · Out of stock, Not enough stock, No longer available, Temporarily unavailablesrc/features/checkout/hooks/checkout-preparation.ts:371
Edge
Must tap Checkout again · Remove & Continue does not restart checkoutsrc/app/(app)/(tabs)/my-bag/index.tsx:425
Edge
Outages not stock errors · 5xx and service unavailable errors skip this modalsrc/features/checkout/hooks/checkout-preparation.ts:92

Shopify checkout

Shopify checkout (iOS)Shopify checkout (Android)
ModalShopifyCheckoutSheet

Shopify checkout

Shopify hosted native sheet for shipping, discount, payment and order placement

src/features/checkout/hooks/use-checkout.ts

Entry

  • Bag Checkout
  • PDP Buy now

Exits

  • Completed with order → order confirmation
  • Close → back on bag or PDP
  • Error → back on bag, no message

States · 12

Gate
Guest prepare fails · prepareCheckout returns Authentication required; bag gates sign-in firstpackages/backend/convex/shopify/checkoutActions.ts:108
Edge
Cart still syncing refusal · Hook returns failure silently; neither caller reads itsrc/features/checkout/hooks/use-checkout.ts:592
Error
Voucher rejected · Voucher belongs to another user or not available in channelpackages/backend/convex/shopify/checkoutActions.ts:108
Edge
Code not applicable only logged · Shopify code not applicable logs a warning, no user messagepackages/backend/convex/shopify/checkoutActions.ts:474
Success
Completed with order id · De-duplicated, session client_completed, bag replaces to confirmationsrc/features/checkout/hooks/use-checkout.ts:264
Edge
Completed without order details · No navigation; bag stays locked: Your order is being confirmedsrc/features/checkout/hooks/use-checkout.ts:322
Edge
Closed without completing · Session abandoned; no toast, no navigationsrc/features/checkout/hooks/use-checkout.ts:359
Error
Shopify error silent · Session abandoned; error stored but nothing reads it, no toastsrc/features/checkout/hooks/use-checkout.ts:383
Error
Present failure silent · presentCheckout returns failure unread by callerssrc/features/checkout/hooks/use-checkout.ts:554
Error
Start failure · Session abandoned and error rethrown to the bag toastsrc/features/checkout/hooks/use-checkout.ts:669
Edge
App killed mid payment · Bag stays locked until Close checkout or 30 min TTL plus 15 min sweeppackages/backend/convex/crons.ts:124
Edge
Webhook clears bag · Cart cleared by orders/create webhook, not locallypackages/backend/convex/webhooks/shopify.ts:1480

Captured states

Shopify checkout, Closed without completing (iOS)Shopify checkout, Closed without completing (Android)
Closed without completing

Bag stamp note

Wireframe

Bag stamp note

Needs data: Needs a cart meeting or missing the stamp minimum; set up cart

InlineBagStampNote

Bag stamp note

Tells shopper whether the bag earns a stamp

src/features/loyalty/components/stamps/bag-stamp-note.tsx

Entry

  • Bag screen, signed-in users only

States · 3

Edge
Hidden · No active campaign, empty bag, or guestsrc/features/loyalty/utils/stamp-eligibility.ts:66
Success
This order earns a stamp · Discounted subtotal meets a campaign minimumsrc/features/loyalty/utils/stamp-eligibility.ts:69
Edge
Spend more to earn a stamp · Shows smallest shortfall adjusted for discountsrc/features/loyalty/utils/stamp-eligibility.ts:69

On this page