Merge pull request #5438 from Matt-Yorkley/cart-stock-ux

Cart stock UX
This commit is contained in:
Luis Ramos
2020-05-22 14:21:16 +01:00
committed by GitHub
13 changed files with 98 additions and 12 deletions

View File

@@ -0,0 +1,2 @@
Darkswarm.controller "CartFormCtrl", ($scope) ->

View File

@@ -0,0 +1,12 @@
# Allows disabling of link buttons via disabled attribute.
# This is normally ignored, ie the link appears disabled but is still clickable.
Darkswarm.directive "disableDynamically", ->
restrict: 'A'
link: (scope, element, attrs) ->
element.on 'click', (e) ->
if attrs.disabled
e.preventDefault()
return

View File

@@ -1,4 +1,4 @@
Darkswarm.directive "ofnOnHand", ->
Darkswarm.directive "ofnOnHand", (StockQuantity) ->
restrict: 'A'
require: "ngModel"
scope: true
@@ -24,6 +24,4 @@ Darkswarm.directive "ofnOnHand", ->
viewValue
scope.available_quantity = ->
on_hand = parseInt(attr.ofnOnHand)
finalized_quantity = parseInt(attr.finalizedquantity) || 0 # finalizedquantity is optional
on_hand + finalized_quantity
StockQuantity.available_quantity(attr.ofnOnHand, attr.finalizedquantity)

View File

@@ -0,0 +1,16 @@
Darkswarm.directive "validateStockQuantity", (StockQuantity) ->
restrict: 'A'
require: "ngModel"
scope: true
link: (scope, element, attr, ngModel) ->
ngModel.$parsers.push (selectedQuantity) ->
valid_number = parseInt(selectedQuantity) != NaN
valid_quantity = parseInt(selectedQuantity) <= scope.available_quantity()
ngModel.$setValidity('stock', (valid_number && valid_quantity) );
selectedQuantity
scope.available_quantity = ->
StockQuantity.available_quantity(attr.ofnOnHand, attr.finalizedquantity)

View File

@@ -0,0 +1,7 @@
Darkswarm.factory "StockQuantity", ->
new class StockQuantity
available_quantity: (on_hand, finalized_quantity) ->
on_hand = parseInt(on_hand)
finalized_quantity = parseInt(finalized_quantity) || 0 # finalized_quantity is optional
on_hand + finalized_quantity

View File

@@ -72,6 +72,12 @@
}
// Shopping cart
#update-cart {
#errorExplanation {
display: none;
}
}
#cart-detail {
.cart-item-delete, .bought-item-delete {
a {
@@ -102,6 +108,12 @@
float: right;
}
}
input {
&.ng-invalid-stock, &.ng-invalid-number {
border: 1px solid $clr-brick;
}
}
}
.item-thumb-image {

View File

@@ -23,7 +23,11 @@
= line_item.single_display_amount_with_adjustments.to_html
%td.text-center.cart-item-quantity{"data-hook" => "cart_item_quantity"}
- finalized_quantity = @order.completed? ? line_item.quantity : 0
= item_form.number_field :quantity, :min => 0, "ofn-on-hand" => "#{variant.on_demand && 9999 || variant.on_hand}", "finalizedquantity" => finalized_quantity, "ng-model" => "line_item_#{line_item.id}", :class => "line_item_quantity", :size => 5
= item_form.number_field :quantity,
:min => 0, "ofn-on-hand" => "#{variant.on_demand && 9999 || variant.on_hand}",
"finalizedquantity" => finalized_quantity, :class => "line_item_quantity", :size => 5,
"ng-model" => "line_item_#{line_item.id}",
"validate-stock-quantity" => true
%td.cart-item-total.text-right{"data-hook" => "cart_item_total"}
= line_item.display_amount_with_adjustments.to_html unless line_item.quantity.nil?

View File

@@ -33,7 +33,8 @@
- else
%div{"data-hook" => "outside_cart_form"}
= form_for @order, :url => main_app.update_cart_path, :html => {:id => 'update-cart'} do |order_form|
= form_for @order, :url => main_app.update_cart_path,
:html => {id: 'update-cart', name: "form", "ng-controller"=> 'CartFormCtrl'} do |order_form|
%div{"data-hook" => "inside_cart_form"}
%div{"data-hook" => "cart_items"}
.row

View File

@@ -1,7 +1,7 @@
%tr
%td{colspan:"2"}
%td
= button_tag :class => 'secondary radius expand small', :id => 'update-button' do
%button#update-button.secondary.radius.expand.small{"ng-class" => "{ alert: form.$dirty && form.$valid }"}
%i.ofn-i_023-refresh
= t(:update)
%td

View File

@@ -1,5 +1,5 @@
.row.links{'data-hook' => "cart_buttons"}
%a.button.large.secondary{href: current_shop_products_path}
%a.continue-shopping.button.large.secondary{href: current_shop_products_path, "ng-disabled" => "#{@insufficient_stock_lines.any?}", "disable-dynamically" => true}
= t :orders_edit_continue
%a#checkout-link.button.large.primary.right{href: main_app.checkout_path}
%a#checkout-link.button.large.primary.right{href: main_app.checkout_path, "ng-disabled" => "#{@insufficient_stock_lines.any?}", "disable-dynamically" => true}
= t :orders_edit_checkout

View File

@@ -3406,7 +3406,7 @@ See the %{link} to find out more about %{sitename}'s features and to start using
format: ! '%Y-%m-%d'
js_format: 'yy-mm-dd'
orders:
error_flash_for_unavailable_items: "An item in your cart has become unavailable."
error_flash_for_unavailable_items: "An item in your cart has become unavailable. Please update the selected quantities."
edit:
login_to_view_order: "Please log in to view your order."
bought:

View File

@@ -161,7 +161,7 @@ describe Spree::OrdersController, type: :controller do
it "displays a flash message when we view the cart" do
spree_get :edit
expect(response.status).to eq 200
expect(flash[:error]).to eq("An item in your cart has become unavailable.")
expect(flash[:error]).to eq I18n.t('spree.orders.error_flash_for_unavailable_items')
end
end
@@ -173,7 +173,7 @@ describe Spree::OrdersController, type: :controller do
it "displays a flash message when we view the cart" do
spree_get :edit
expect(response.status).to eq 200
expect(flash[:error]).to eq("An item in your cart has become unavailable.")
expect(flash[:error]).to eq I18n.t('spree.orders.error_flash_for_unavailable_items')
end
end
end

View File

@@ -213,6 +213,40 @@ feature "full-page cart", js: true do
expect(page).to have_content "Insufficient stock available, only 2 remaining"
expect(page).to have_field "order_line_items_attributes_0_quantity", with: '1'
end
describe "full UX for correcting selected quantities with insufficient stock" do
before do
add_product_to_cart order, product_with_tax, quantity: 5
variant.update_attributes! on_hand: 4, on_demand: false
end
it "gives clear user feedback during the correcting process" do
visit main_app.cart_path
# shows a relevant Flash message
expect(page).to have_selector ".alert-box", text: I18n.t('spree.orders.error_flash_for_unavailable_items')
# "Continue Shopping" and "Checkout" buttons are disabled
expect(page).to have_selector "a.continue-shopping[disabled=disabled]"
expect(page).to have_selector "a#checkout-link[disabled=disabled]"
# Quantity field clearly marked as invalid and "Update" button is not highlighted
expect(page).to have_selector "#order_line_items_attributes_0_quantity.ng-invalid-stock"
expect(page).to_not have_selector "#update-button.alert"
fill_in "order_line_items_attributes_0_quantity", with: 4
# Quantity field not marked as invalid and "Update" button is highlighted after correction
expect(page).to_not have_selector "#order_line_items_attributes_0_quantity.ng-invalid-stock"
expect(page).to have_selector "#update-button.alert"
click_button I18n.t("update")
# "Continue Shopping" and "Checkout" buttons are not disabled after cart is updated
expect(page).to_not have_selector "a.continue-shopping[disabled=disabled]"
expect(page).to_not have_selector "a#checkout-link[disabled=disabled]"
end
end
end
end