Created
April 12, 2023 14:37
-
-
Save IliasDeros/240345a9a3a8bc4c828bc3e8715af320 to your computer and use it in GitHub Desktop.
Generated by XState Viz: https://xstate.js.org/viz
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// First step is "type" only when the order type was never selected | |
const newOrder = (context) => { | |
return !context.order.get('order_type') | |
}; | |
// No order type selected | |
// No line-item errors | |
// No store selected for pickup | |
// No delivery day for delivery order | |
// No shipping fee for shipping order | |
const detailsAvailable = (context) => { | |
const { hasLineItemErrors, order } = context; | |
const type = order.get('order_type') | |
const noValidPickupStoreSelected = | |
type === "pickup" && !context.selectedStore; | |
const noValidDeliveryZone = (order) => { | |
if (type !== "delivery") { | |
return false; | |
} | |
const isPastDate = (yyyyMmDd, hhmm, cutoffMs) => { | |
const [y, m, d] = yyyyMmDd.split("/"); | |
const [h, mm] = hhmm.split(":"); | |
const now = new Date(); | |
const deliveryDate = new Date( | |
+y, | |
+m - 1, // the month is 0-indexed | |
+d, | |
+h, | |
+mm | |
); | |
return (deliveryDate - cutoffMs) < now; | |
}; | |
const { date, from, cutoff_ms } = order.get('delivery_day') || {}; | |
return !date || isPastDate(date, from, cutoff_ms); | |
}; | |
const noShippingFee = | |
type === "shipping" && [null, undefined].includes(order.get('shipping_fee')); | |
return ( | |
!newOrder(context) && | |
!hasLineItemErrors && | |
!noValidPickupStoreSelected && | |
!noValidDeliveryZone(order) && | |
!noShippingFee | |
); | |
}; | |
const paymentAvailable = (context) => { | |
const previousStepAvailable = detailsAvailable(context); | |
return previousStepAvailable && context.order.get('user.id'); | |
}; | |
const confirmAvailable = (context) => { | |
const previousStepAvailable = paymentAvailable(context); | |
const payment = context.order.get('payment') || {} | |
const paypalSelected = payment === "paypal"; | |
const gpaySelected = payment === "gpay"; | |
const stripeSelected = payment.token || payment.use_saved; | |
const paymentValid = paypalSelected || stripeSelected || gpaySelected; | |
return previousStepAvailable && paymentValid; | |
}; | |
const not = (transitionGuard) => function() { return !transitionGuard(...arguments) }; | |
const detailsTransition = { | |
target: "details", | |
cond: detailsAvailable, | |
}; | |
const detailsUnavailableTransition = { | |
target: "type", | |
cond: not(detailsAvailable) | |
} | |
const paymentTransition = { | |
target: "payment", | |
cond: paymentAvailable | |
}; | |
const paymentUnavailableTransition = { | |
target: "type", | |
cond: not(paymentAvailable) | |
} | |
const confirmTransition = { | |
target: "confirmation", | |
cond: confirmAvailable, | |
}; | |
const confirmUnavailableTransition = { | |
target: "type", | |
cond: not(confirmAvailable), | |
}; | |
const tabTransitions = { | |
TYPE: "type", | |
REVIEW: "review", | |
DETAILS: [detailsTransition, detailsUnavailableTransition], | |
PAYMENT: [paymentTransition, paymentUnavailableTransition], | |
CONFIRMATION: [confirmTransition, confirmUnavailableTransition], | |
}; | |
const setOrder = assign({ | |
order: (context, event) => { | |
return event.order | |
}, | |
}) | |
const selectStore = assign({ | |
selectedStore: (coexntext, event) => { | |
return event.store | |
} | |
}) | |
// xstate/fsm does not support this: createMachine({ on: eventlessTransitions }) | |
const eventlessTransitions = { | |
// Line item actions required because of the async line-items relationship | |
LINE_ITEM_ERROR: [ | |
{ | |
actions: assign({ hasLineItemErrors: true }), | |
}, | |
], | |
LINE_ITEM_VALID: [ | |
{ | |
actions: assign({ hasLineItemErrors: false }), | |
}, | |
], | |
LINE_ITEMS_EMPTY: { | |
target: "emptyCart", | |
actions: assign({ hasLineItemErrors: false }), | |
}, | |
SET_ORDER: { actions: [setOrder] }, | |
SELECT_PICKUP_STORE: { actions: [selectStore] } | |
} | |
const checkoutStepMachine = Machine( | |
{ | |
id: "Checkout Step", | |
initial: "emptyCart", | |
context: { | |
hasLineItemErrors: false, | |
selectedStore: false, | |
order: { | |
get(){} | |
}, | |
}, | |
states: { | |
emptyCart: { | |
on: Object.assign({ | |
// Always type first to force customers to reconfirm their choice of order type | |
ADD_TO_CART: "type" | |
}, tabTransitions, eventlessTransitions), | |
}, | |
type: { | |
on: Object.assign({ | |
NEXT: "review" | |
}, tabTransitions, eventlessTransitions), | |
}, | |
review: { | |
on: Object.assign({ | |
NEXT: detailsTransition, | |
}, tabTransitions, eventlessTransitions), | |
}, | |
details: { | |
on: Object.assign({ | |
NEXT: paymentTransition, | |
}, tabTransitions, eventlessTransitions), | |
}, | |
payment: { | |
on: Object.assign({ | |
NEXT: confirmTransition, | |
}, tabTransitions, eventlessTransitions), | |
}, | |
confirmation: { | |
on: Object.assign({ | |
PAYMENT_SUCCESSFUL: "emptyCart", | |
}, tabTransitions, eventlessTransitions), | |
}, | |
}, | |
} | |
); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment