Skip to content

Instantly share code, notes, and snippets.

@IliasDeros
Created April 12, 2023 14:37
Show Gist options
  • Save IliasDeros/240345a9a3a8bc4c828bc3e8715af320 to your computer and use it in GitHub Desktop.
Save IliasDeros/240345a9a3a8bc4c828bc3e8715af320 to your computer and use it in GitHub Desktop.
Generated by XState Viz: https://xstate.js.org/viz
// 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