Skip to content

Instantly share code, notes, and snippets.

@theotherzach
Last active July 11, 2016 14:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save theotherzach/92ddb73af8f9e4c8ab70ca82a49c8fc1 to your computer and use it in GitHub Desktop.
Save theotherzach/92ddb73af8f9e4c8ab70ca82a49c8fc1 to your computer and use it in GitHub Desktop.
;(function () {
"use strict"
var app = window.app
var actions = {
ajaxError: function (store, message, err, type) {
store.dispatch("AJAX_ERROR", message, err, type)
},
cancelEditShipping: function (store, isInStore) {
store.dispatch("TOGGLE_EDIT_SHIPPING", false)
if (isInStore) {
store.dispatch("REVERT_PICKUP_DETAILS")
return
}
store.dispatch("REVERT_SHIPPING_ADDRESS")
},
creditCardFormValid: function (store) {
if (store.state.isCreditCardFormValid) { return }
store.dispatch("CREDIT_CARD_FORM_VALID")
},
creditCardFormInvalid: function (store) {
if (!store.state.isCreditCardFormValid) { return }
store.dispatch("CREDIT_CARD_FORM_INVALID")
},
newShipping: function (store) {
store.dispatch("SET_SHIPPING_ADDRESS", {})
store.dispatch("TOGGLE_EDIT_SHIPPING", true)
},
editShipping: function (store, address) {
if (address) {
store.dispatch("SET_SHIPPING_ADDRESS", address)
}
store.dispatch("TOGGLE_EDIT_SHIPPING", true)
},
handleAddressFormValid: function (store) {
store.dispatch("TOGGLE_ADDRESS_FORM_VALID", true)
},
handleAddressFormInvalid: function (store) {
store.dispatch("TOGGLE_ADDRESS_FORM_VALID", false)
},
getOrder: function (store) {
app.services.ordersService.get()
.then(function (data) {
store.dispatch("SET_ORDER", data)
}, function (err) {
store.dispatch("AJAX_ERROR", "Could not get Cart. Please reload the page", err.responseJSON)
})
},
destroyLineItem: function (store, orderLineItem) {
app.services.orderLineItemsService.destroy(orderLineItem.id)
.then(function (data) {
return app.services.ordersService.get()
}, function (err) {
store.dispatch("AJAX_ERROR", "Could not remove product", err.responseJSON, ["orderLineItem", "Line Item"])
})
.then(function (data) {
store.dispatch("SET_ORDER", data)
}, function (err) {
store.dispatch("AJAX_ERROR", "Product removed but we had trouble updating your cart totals. Please reload the page")
})
},
setReusedCardId: function (store, cardId) {
store.dispatch("SET_REUSED_CARD_ID", cardId)
},
/**
* @param {Object} store
* @param {Object} shippingMethod
* @returns {Promise.<ShippingMethod|Error>}
*/
setShippingMethod: function (store, shippingMethod) {
var savedShippingMethod = Object.assign({}, store.state.shippingMethod)
store.dispatch("SAVE_SHIPPING_METHOD", shippingMethod)
return app.services.shippingMethodsService.save(shippingMethod.name)
.then(function (data) {
return Promise.resolve(data)
}, function (err) {
store.dispatch("SAVE_SHIPPING_METHOD_FAILURE", savedShippingMethod)
store.dispatch("AJAX_ERROR", null, null, ["shippingMethod", "Shipping Method"] )
return Promise.reject(err)
})
},
setPayment: function (store, cardParams) {
var cardClone = Object.assign({}, cardParams)
var ccn = cardClone.credit_card_number
if (ccn.length > 4) {
cardClone.last4 = ccn.substr(ccn.length - 4, 4)
} else {
cardClone.last4 = ccn
}
store.dispatch("TOGGLE_EDIT_CREDIT_CARD", false)
store.dispatch("SET_CARD", cardClone)
},
closeCardForm: function (store) {
store.dispatch("TOGGLE_EDIT_CREDIT_CARD", false)
},
openCardForm: function (store) {
store.dispatch("CHECKPOINT_CARD", true)
store.dispatch("TOGGLE_EDIT_CREDIT_CARD", true)
},
/**
* @param {Object} store
* @param {Number} userAddressId
* @returns {undefined}
*/
setReusedAddressId: function (store, userAddressId) {
store.dispatch("INVALIDATE_SHIPPING_METHOD")
store.dispatch("SET_REUSED_ADDRESS_ID", userAddressId)
},
setUserAddresses: function (store, data) {
store.dispatch("SET_USER_ADDRESSES", data)
},
persistItem: function (store, itemParams) {
store.dispatch("SET_ITEM", itemParams)
app.services.orderLineItemsService.save(itemParams)
.then(function (data) {
store.dispatch("SET_ITEM", data)
return app.services.ordersService.get()
}, function (err) {
store.dispatch("REVERT_ITEM", itemParams.id)
store.dispatch("AJAX_ERROR", null , err.responseJSON, ["orderLineItem", "Line Item"])
})
.then(function (data) {
store.dispatch("SET_ORDER", data)
}, function (err) {
store.dispatch("AJAX_ERROR", "Product changed but we had trouble updating your cart totals. Please reload the page", err.responseJSON)
})
},
/**
* Coordinates network calls, responses, and resulting
* mutations for finalizing an order.
* @param {Object} store
* @returns Promise.{(undefined|Error)}
*/
placeOrder: function (store) {
if (store.state.isWaitingForFinish) { return }
if (!app.getters.isReviewDisplayed(store.state)) { return }
store.dispatch("TOGGLE_IS_WAITING_FOR_FINISH", true)
return app.services.ordersService.finish().then(function (data) {
store.dispatch("TOGGLE_IS_WAITING_FOR_FINISH", false)
return data
}, function (err) {
store.dispatch("TOGGLE_IS_WAITING_FOR_FINISH", false)
var errorObject = err.responseJSON
if (err.status !== 422) {
errorObject = null
}
if (errorObject && errorObject.credit_card) {
if (app.getters.hasEnteredNewCard(store.state)) {
store.dispatch("CHECKPOINT_CARD", true)
store.dispatch("TOGGLE_EDIT_CREDIT_CARD", true)
} else {
store.dispatch("REUSED_CARD_ERROR")
}
}
store.dispatch("AJAX_ERROR", null, errorObject, ["finishOrder", "Place Order"])
return err
})
},
/**
* @param {Object} store
* @param {Number} userAddressId
* @returns {undefined}
*/
updateShippingAddress: function (store, payload) {
store.dispatch("SET_SHIPPING_ADDRESS", payload)
if (store.state.reusedAddressId) {
store.dispatch("SET_REUSED_ADDRESS_ID", null)
}
if (store.state.isEditingAddress) {
store.dispatch("TOGGLE_EDIT_SHIPPING", false)
}
if (store.state.order.shipping_method) {
store.dispatch("INVALIDATE_SHIPPING_METHOD")
}
},
setShippingMethods: function(store, shippingMethods) {
store.dispatch("SET_SHIPPING_METHODS", shippingMethods)
},
updateInStorePickupInfo: function (store, payload) {
store.dispatch("TOGGLE_EDIT_SHIPPING", false)
store.dispatch("SET_PICKUP", payload)
},
viewInStoreSelect: function (store) {
store.dispatch("TOGGLE_EDIT_SHIPPING", true)
store.dispatch("SET_ONLY_IN_STORE")
},
checkPromo: function (store, promoCode) {
},
}
app.actions = actions
})();
;(function () {
"use strict"
var app = window.app
var getters = {
creditCard: function (state) {
return state.creditCard
},
COUNTRIES: function (state) {
return state.COUNTRIES
},
errors: function (state) {
return state.errors
},
hasEmailAddress: function (state) {
return state.user.email != undefined
},
hasEnteredNewCard: function (state) {
return !(state.creditCard.last4 === undefined || state.creditCard.last4 === null)
},
/*
* @returns {Boolean}
*/
hasAttachedPayment: function (state) {
if (app.getters.hasEnteredNewCard(state)) {
return true
}
return state.reusedCardId !== null
},
hasSelectedInStore: function (state) {
return !(state.pickupDetails.store_location_id === undefined || state.pickupDetails.store_location_id === null)
},
hasAttachedShippingAddress: function (state) {
return !(state.order.shipping_address_id === undefined || state.order.shipping_address_id === null)
},
isAddressFormValid: function (state) {
return state.isAddressFormValid
},
isCreditCardFormVisible: function (state) {
if (state.hasActivatedEditCreditCard !== null) {
return state.hasActivatedEditCreditCard
}
if (app.getters.paymentMethods(state)[0]) {
return false
}
return !app.getters.hasAttachedPayment(state)
},
isCreditCardFormValid: function (state) {
return state.isCreditCardFormValid
},
isEmptyCart: function (state) {
return state.order.id != undefined && (!state.order.order_line_items ||
!state.order.order_line_items[0])
},
isBillingDisplayed: function (state) {
return app.getters.isShippingDisplayed(state) &&
(state.order.shipping_method ||
app.getters.hasSelectedInStore(state))
},
isEditingAddress: function (state) {
return state.isEditingAddress
},
isOnlySelectingInStore: function (state) {
return state.isOnlySelectingInStore
},
isOrderLoading: function (state) {
return state.order.id === undefined || state.order.id === null
},
isReusingAddress: function (state) {
return state.reusedAddressId !== null
},
isReviewDisplayed: function (state) {
return app.getters.isBillingDisplayed(state) &&
app.getters.hasAttachedPayment(state)
},
isReviewOrderButtonDisplayed: function (state) {
if (app.getters.hasAttachedPayment(state)) {
return false
}
return true
},
/**
* @param {Object} store
* @returns {Boolean}
*/
isShippingMethodSelected: function (state) {
return state.order.shipping_method !== ""
},
isShippingDisplayed: function (state) {
return !app.getters.isOrderLoading(state) && app.getters.hasEmailAddress(state)
},
isUsingNewAddress: function (state) {
if (!app.getters.hasAttachedShippingAddress(state)) {
return false
}
return !app.getters.isReusingAddress(state)
},
isWaitingForFinish: function (state) {
return state.isWaitingForFinish
},
order: function (state) {
return state.order
},
paymentMethods: function (state) {
return state.paymentMethods
},
pickupDetails: function (state) {
return state.pickupDetails
},
/**
* When the server returns a credit card error on finalize,
* and the user is attempting to reuse a card, that UserCard's
* id is pushed into this array.
* @param {Object} state
* @returns {Array<Number>}
*/
problemCardIds: function (state) {
return state.problemCardIds
},
reusedAddressId: function (state) {
return state.reusedAddressId
},
reusedCardId: function (state) {
return state.reusedCardId
},
shippingAddress: function (state) {
return state.shippingAddress
},
shippingContact: function (state) {
return state.shippingContact
},
shippingMethods: function (state) {
return state.shippingMethods
},
shippingPromoDollars: function (state) {
var toDollars = Vue.filter("toDollars")
return toDollars(state.shippingPromoCents)
},
STATES: function (state) {
return state.STATES
},
storeLocations: function (state) {
return state.storeLocations
},
toQualifyDollars: function (state) {
var toDollars = Vue.filter("toDollars")
var difference = toDollars(state.shippingPromoCents - state.order.subtotal_cents)
if (difference <= 0) {
return 0
}
return difference
},
user: function (state) {
return state.user
},
userAddresses: function (state) {
return state.userAddresses
}
}
app.getters = getters
})();
;(function () {
"use strict"
var app = window.app
var gon = window.gon
var HARD_CODED_STORE_LOCATIONS = [{ // FIXME: Hard coded
name: "***",
id: 1
}, {
name: "Option 2",
id: 2
}, {
name: "Option 3",
id: 3
}]
var HARD_CODED_SHIPPING_PROMO_CENTS = 5000 // FIXME: Hard coded
var store = {
state: {
hasActivatedEditCreditCard: null,
isAddressFormValid: false,
isWaitingForFinish: false,
isCreditCardFormValid: false,
isEditingAddress: false,
isOnlySelectingInStore: false,
COUNTRIES: app.constants.COUNTRIES,
user: gon.current_user,
userAddresses: gon.user_addresses,
order: gon.order,
pickupDetails: {},
shippingContact: {
first_name: undefined,
last_name: undefined,
phone: undefined
},
shippingAddress: {},
reusedAddressId: null,
creditCard: {},
paymentMethods: gon.payment_methods,
reusedCardId: null,
problemCardIds: [],
shippingMethods: [],
shippingPromoCents: HARD_CODED_SHIPPING_PROMO_CENTS,
STATES: app.constants.STATES,
storeLocations: HARD_CODED_STORE_LOCATIONS,
checkpoints: {},
clones: {
items: {},
order: {},
shippingAddress: {},
pickupDetails: {}
},
errors: []
},
middlewares: [{
onInit: function (state, store) {
var shippingContactKeys = Object.keys(state.shippingContact)
shippingContactKeys.forEach(function (key) {
state.shippingContact[key] = gon.shipping_address[key] || gon.current_user[key]
})
var shippingAddress = Object.keys(gon.shipping_address)
.filter(function (key) {
return shippingContactKeys.indexOf(key) === -1
})
.reduce(function (memo, key) {
memo[key] = gon.shipping_address[key]
return memo
}, {})
state.shippingAddress = shippingAddress
state.clones.order = Object.assign({}, state.order)
state.clones.shippingAddress = Object.assign({}, state.shippingAddress)
state.clones.pickupDetails = Object.assign({}, state.pickupDetails)
if (state.order.id) {
state.reusedAddressId = app.services.shippingAddressesService.reusedId(state.order.id)
state.reusedCardId = app.services.paymentsService.reusedId(state.order.id)
}
state.checkpoints = {
reusedCardId: state.reusedCardId,
creditCard: state.creditCard
}
}
}],
mutations: {
AJAX_ERROR: function (state, message, err, type) {
state.errors.push({
message: message,
errorPayload: err,
type: type
})
},
CHECKPOINT_CARD: function (state) {
state.checkpoints.creditCard = Object.assign({}, state.creditCard)
},
CREDIT_CARD_FORM_VALID: function (state) {
state.isCreditCardFormValid = true
},
CREDIT_CARD_FORM_INVALID: function (state) {
state.isCreditCardFormValid = false
},
/**
* @param {Object} state
* @returns {undefined}
*/
INVALIDATE_SHIPPING_METHOD: function (state) {
state.order.shipping_method = ""
state.order.shipping_total_cents = 0
},
/**
* @param {Object} state
* @returns {undefined}
*/
REUSED_CARD_ERROR: function (state) {
state.problemCardIds.push(state.reusedCardId)
},
SET_CARD: function (state, card) {
state.reusedCardId = null
state.creditCard = card
},
SET_ORDER: function (state, order) {
state.order = order
},
SET_PICKUP: function (state, pickupParams) {
state.clones.pickupDetails = Object.assign({}, state.pickupDetails)
var newPickupDetails = Object.assign({}, pickupParams)
state.shippingContact = {
first_name: newPickupDetails.first_name,
last_name: newPickupDetails.last_name,
phone: newPickupDetails.phone
}
delete newPickupDetails.first_name
delete newPickupDetails.last_name
delete newPickupDetails.phone
state.pickupDetails = newPickupDetails
state.shippingAddress = {}
state.order.shipping_address_id = null
},
SET_REUSED_CARD_ID: function (state, cardId) {
state.creditCard = {}
state.reusedCardId = cardId
},
NEW_SHIPPING_ADDRESS: function (state) {
state.clones.shippingAddress = Object.assign({}, state.shippingAddress)
state.shippingAddress = Object.keys(state.shippingAddress)
.reduce(function(memo, key) {
if (key === "id" || key.match(/_id$/)) {
memo[key] = undefined
return memo
}
memo[key] = ""
return memo
}, {})
},
/*
* @param {Object} state
* @param {ShippingMethod} shippingMethod
* @returns {undefined}
*/
SAVE_SHIPPING_METHOD: function (state, shippingMethod) {
state.order.shipping_total_cents = shippingMethod.cost_cents
state.order.shipping_method = shippingMethod.name
},
/*
* @param {Object} state
* @param {ShippingMethod} shippingMethod
* @returns {undefined}
*/
SAVE_SHIPPING_METHOD_FAILURE: function (state, originalShippingMethod) {
state.order.shipping_total_cents = originalShippingMethod.cost_cents
state.order.shipping_method = originalShippingMethod.name
},
SET_SHIPPING_ADDRESS: function (state, addressParams) {
state.clones.shippingAddress = Object.assign({}, state.shippingAddress)
var newShippingAddress = Object.assign({}, state.shippingContact, addressParams)
state.shippingContact = {
first_name: newShippingAddress.first_name || state.user.first_name,
last_name: newShippingAddress.last_name || state.user.last_name,
phone: newShippingAddress.phone
}
delete newShippingAddress.first_name
delete newShippingAddress.last_name
delete newShippingAddress.phone
state.shippingAddress = newShippingAddress
state.pickupDetails = {}
state.order.shipping_address_id = state.shippingAddress.id
},
SET_SHIPPING_METHODS: function(state, shippingMethods) {
state.shippingMethods = shippingMethods
},
SET_USER_ADDRESSES: function (state, addresses) {
state.userAddresses = addresses
},
SET_REUSED_ADDRESS_ID: function (state, userAddressId) {
state.reusedAddressId = userAddressId
},
REVERT_SHIPPING_ADDRESS: function (state) {
state.shippingAddress = Object.assign({}, state.clones.shippingAddress)
},
SET_ITEM: function (state, itemParams) {
var originalItem = state.order.order_line_items.find(function(item) {
return item.id === itemParams.id
})
// Push the item onto the array and return if originalItem
// is not found.
if (!originalItem) {
state.order.order_line_items.push(itemParams)
return
}
// Create a backup of the original item
var clonedItem = Object.assign({}, originalItem)
state.clones.items[clonedItem.id] = clonedItem
state.clones.items = Object.assign({}, state.clones.items)
// Replace the original item with an updated copy
var index = state.order.order_line_items.indexOf(originalItem)
var updatedItem = Object.assign({}, originalItem, itemParams)
state.order.order_line_items.$set(index, updatedItem)
},
SET_ONLY_IN_STORE: function (state) {
state.isOnlySelectingInStore = true
},
REVERT_ITEM: function (state, id) {
if (!id) { return }
var currentItem = state.order.order_line_items.find(function(item) {
return item.id === id
})
// Return early if we cannot find the current item
if (!currentItem) {
return
}
// Replace the current item with a clone of the backup
var previousItem = state.clones.items[currentItem.id]
var clonedPreviousItem = Object.assign({}, previousItem)
var index = state.order.order_line_items.indexOf(currentItem)
state.order.order_line_items.$set(index, clonedPreviousItem)
},
REVERT_PICKUP_DETAILS: function (state) {
state.pickupDetails = Object.assign({}, state.clones.pickupDetails)
},
TOGGLE_ADDRESS_FORM_VALID: function (state, isValid) {
if (isValid === undefined) {
state.isAddressFormValid = !state.isAddressFormValid
return
}
state.isAddressFormValid = isValid
},
TOGGLE_IS_WAITING_FOR_FINISH: function (state, isWaiting) {
if (isWaiting === undefined) {
state.isWaitingForFinish = !state.isWaitingForFinish
return
}
state.isWaitingForFinish = isWaiting
},
TOGGLE_EDIT_CREDIT_CARD: function (state, shouldEdit) {
if (shouldEdit === undefined) {
state.hasActivatedEditCreditCard = !state.hasActivatedEditCreditCard
return
}
state.hasActivatedEditCreditCard = shouldEdit
},
TOGGLE_EDIT_SHIPPING: function(state, shouldEdit) {
if (shouldEdit === undefined) {
state.isEditingAddress = !state.isEditingAddress
} else {
state.isEditingAddress = shouldEdit
}
if (!state.isEditingAddress) {
state.isOnlySelectingInStore = false
}
},
}
}
app.store = store
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment