Merge pull request #5722 from rioug/4206-back-from-Edit-Product-removed-filters-products-page

4206 back from edit product removed filters products page
This commit is contained in:
Luis Ramos
2020-09-17 11:51:58 +01:00
committed by GitHub
32 changed files with 832 additions and 185 deletions

View File

@@ -1,4 +1,4 @@
angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout, $filter, $http, $window, BulkProducts, DisplayProperties, DirtyProducts, VariantUnitManager, StatusMessage, producers, Taxons, Columns, tax_categories, RequestMonitor, SortOptions, ErrorsParser) ->
angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout, $filter, $http, $window, $location, BulkProducts, DisplayProperties, DirtyProducts, VariantUnitManager, StatusMessage, producers, Taxons, Columns, tax_categories, RequestMonitor, SortOptions, ErrorsParser, ProductFiltersUrl) ->
$scope.StatusMessage = StatusMessage
$scope.columns = Columns.columns
@@ -13,37 +13,29 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout
{id: 100, name: t('js.admin.orders.index.per_page', results: 100)}
]
$scope.filterableColumns = [
{ name: t("label_producers"), db_column: "producer_name" },
{ name: t("name"), db_column: "name" }
]
$scope.filterTypes = [
{ name: t("equals"), predicate: "eq" },
{ name: t("contains"), predicate: "cont" }
]
$scope.optionTabs =
filters: { title: t("filter_products"), visible: false }
$scope.q = {
producerFilter: ""
categoryFilter: ""
importDateFilter: ""
query: ""
sorting: ""
}
$scope.producers = producers
$scope.taxons = Taxons.all
$scope.tax_categories = tax_categories
$scope.producerFilter = ""
$scope.categoryFilter = ""
$scope.importDateFilter = ""
$scope.page = 1
$scope.per_page = 15
$scope.products = BulkProducts.products
$scope.query = ""
$scope.DisplayProperties = DisplayProperties
$scope.sortOptions = SortOptions
$scope.initialise = ->
$scope.q = ProductFiltersUrl.loadFromUrl($location.search())
$scope.fetchProducts()
$scope.$watchCollection '[query, producerFilter, categoryFilter, importDateFilter, per_page]', ->
$scope.$watchCollection '[q.query, q.producerFilter, q.categoryFilter, q.importDateFilter, per_page]', ->
$scope.page = 1 # Reset page when changing filters for new search
$scope.changePage = (newPage) ->
@@ -53,25 +45,27 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout
$scope.fetchProducts = ->
removeClearedValues()
params = {
'q[name_cont]': $scope.query,
'q[supplier_id_eq]': $scope.producerFilter,
'q[primary_taxon_id_eq]': $scope.categoryFilter,
'q[s]': $scope.sorting,
import_date: $scope.importDateFilter,
'q[name_cont]': $scope.q.query,
'q[supplier_id_eq]': $scope.q.producerFilter,
'q[primary_taxon_id_eq]': $scope.q.categoryFilter,
'q[s]': $scope.q.sorting,
import_date: $scope.q.importDateFilter,
page: $scope.page,
per_page: $scope.per_page
}
RequestMonitor.load(BulkProducts.fetch(params).$promise).then ->
# update url with the filters used
$location.search(ProductFiltersUrl.generate($scope.q))
$scope.resetProducts()
removeClearedValues = ->
delete $scope.producerFilter if $scope.producerFilter == "0"
delete $scope.categoryFilter if $scope.categoryFilter == "0"
delete $scope.importDateFilter if $scope.importDateFilter == "0"
delete $scope.q.producerFilter if $scope.q.producerFilter == "0"
delete $scope.q.categoryFilter if $scope.q.categoryFilter == "0"
delete $scope.q.importDateFilter if $scope.q.importDateFilter == "0"
$timeout ->
if $scope.showLatestImport
$scope.importDateFilter = $scope.importDates[1].id
$scope.q.importDateFilter = $scope.importDates[1].id
$scope.resetProducts = ->
DirtyProducts.clear()
@@ -101,10 +95,10 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout
$scope.visibleTab = tab
$scope.resetSelectFilters = ->
$scope.query = ""
$scope.producerFilter = "0"
$scope.categoryFilter = "0"
$scope.importDateFilter = "0"
$scope.q.query = ""
$scope.q.producerFilter = "0"
$scope.q.categoryFilter = "0"
$scope.q.importDateFilter = "0"
$scope.fetchProducts()
$scope.$watch 'sortOptions', (sort) ->
@@ -122,8 +116,7 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout
$scope.editWarn = (product, variant) ->
if confirm_unsaved_changes()
window.open(editProductUrl(product, variant), "_blank")
$window.location.href = ProductFiltersUrl.buildUrl(editProductUrl(product, variant), $scope.q)
$scope.toggleShowAllVariants = ->
showVariants = !DisplayProperties.showVariants 0
@@ -220,10 +213,10 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout
data:
products: productsToSubmit
filters:
'q[name_cont]': $scope.query
'q[supplier_id_eq]': $scope.producerFilter
'q[primary_taxon_id_eq]': $scope.categoryFilter
import_date: $scope.importDateFilter
'q[name_cont]': $scope.q.query
'q[supplier_id_eq]': $scope.q.producerFilter
'q[primary_taxon_id_eq]': $scope.q.categoryFilter
import_date: $scope.q.importDateFilter
page: $scope.page
per_page: $scope.per_page
).success((data) ->

View File

@@ -5,5 +5,10 @@ angular.module("ofn.admin").directive "ofnSelect2MinSearch", ->
minimumResultsForSearch: attrs.ofnSelect2MinSearch
ngModel.$formatters.push (value) ->
if (value)
element.select2('val', value);
# select2 populates options with a value like "number:3" or "string:category" but
# select2('val', value) doesn't do the type conversion for us as one would expect
if isNaN(value)
element.select2('val', "string:#{value}")
else
element.select2('val', "number:#{value}")

View File

@@ -0,0 +1,23 @@
angular.module("ofn.admin").factory "ProductFiltersUrl", ($httpParamSerializer) ->
new class ProductFiltersUrl
productFilters: ['producerFilter', 'categoryFilter', 'query', 'sorting', 'importDateFilter']
loadFromUrl: (filters) ->
loadedFilters = {}
for filter in @productFilters
loadedFilters[filter] = if filters[filter] then filters[filter] else ""
loadedFilters
generate: (ctrlFilters) ->
filters = {}
for filter in @productFilters
filters[filter] = ctrlFilters[filter] if ctrlFilters[filter]
filters
buildUrl: (baseUrl, ctrlFilters) ->
filterUrl = $httpParamSerializer(@generate(ctrlFilters))
filterUrl = "?#{filterUrl}" if filterUrl isnt ""
"#{baseUrl}#{filterUrl}"

View File

@@ -8,9 +8,55 @@ module Spree
before_action :load_data
create.before :set_viewable
update.before :set_viewable
destroy.before :destroy_before
def index
@url_filters = ::ProductFilters.new.extract(request.query_parameters)
end
def new
@url_filters = ::ProductFilters.new.extract(request.query_parameters)
render layout: !request.xhr?
end
def create
@url_filters = ::ProductFilters.new.extract(params)
set_viewable
@object.attributes = permitted_resource_params
if @object.save
flash[:success] = flash_message_for(@object, :successfully_created)
redirect_to admin_product_images_url(params[:product_id], @url_filters)
else
respond_with(@object)
end
end
def edit
@url_filters = ::ProductFilters.new.extract(request.query_parameters)
end
def update
@url_filters = ::ProductFilters.new.extract(params)
set_viewable
if @object.update(permitted_resource_params)
flash[:success] = flash_message_for(@object, :successfully_updated)
redirect_to admin_product_images_url(params[:product_id], @url_filters)
else
respond_with(@object)
end
end
def destroy
@url_filters = ::ProductFilters.new.extract(request.query_parameters)
destroy_before
if @object.destroy
flash[:success] = flash_message_for(@object, :successfully_removed)
end
redirect_to admin_product_images_url(params[:product_id], @url_filters)
end
private

View File

@@ -5,6 +5,20 @@ module Spree
before_action :find_properties
before_action :setup_property, only: [:index]
def index
@url_filters = ::ProductFilters.new.extract(request.query_parameters)
end
def destroy
@url_filters = ::ProductFilters.new.extract(request.query_parameters)
if @object.destroy
flash[:success] = flash_message_for(@object, :successfully_removed)
end
# if destroy fails it won't show any errors to the user
redirect_to admin_product_product_properties_url(params[:product_id], @url_filters)
end
private
def find_properties

View File

@@ -10,35 +10,32 @@ module Spree
include OrderCyclesHelper
include EnterprisesHelper
create.before :create_before
update.before :update_before
before_action :load_data
before_action :load_form_data, only: [:index, :new, :create, :edit, :update]
before_action :load_spree_api_key, only: [:index, :variant_overrides]
before_action :strip_new_properties, only: [:create, :update]
respond_override create: { html: {
success: lambda {
if params[:button] == "add_another"
redirect_to new_admin_product_path
else
redirect_to admin_products_path
end
},
failure: lambda {
render :new
}
} }
def new
@object.shipping_category = DefaultShippingCategory.find_or_create
super
end
def create
delete_stock_params_and_set_after do
super
if params[:product][:prototype_id].present?
@prototype = Spree::Prototype.find(params[:product][:prototype_id])
end
@object.attributes = permitted_resource_params
if @object.save
flash[:success] = flash_message_for(@object, :successfully_created)
if params[:button] == "add_another"
redirect_to new_admin_product_path
else
redirect_to admin_products_path
end
else
render :new
end
end
rescue Paperclip::Errors::NotIdentifiedByImageMagickError
invoke_callbacks(:create, :fails)
@@ -56,14 +53,24 @@ module Spree
@show_latest_import = params[:latest_import] || false
end
def update
original_supplier_id = @product.supplier_id
def edit
@url_filters = ::ProductFilters.new.extract(params)
end
def update
@url_filters = ::ProductFilters.new.extract(request.query_parameters)
original_supplier_id = @product.supplier_id
delete_stock_params_and_set_after do
super
if original_supplier_id != @product.supplier_id
ExchangeVariantDeleter.new.delete(@product)
params[:product] ||= {} if params[:clear_product_properties]
if @object.update(permitted_resource_params)
if original_supplier_id != @product.supplier_id
ExchangeVariantDeleter.new.delete(@product)
end
flash[:success] = flash_message_for(@object, :successfully_updated)
end
redirect_to edit_admin_product_url(@object, @url_filters)
end
end
@@ -93,6 +100,14 @@ module Spree
redirect_to edit_admin_product_url(@new)
end
def group_buy_options
@url_filters = ::ProductFilters.new.extract(request.query_parameters)
end
def seo
@url_filters = ::ProductFilters.new.extract(request.query_parameters)
end
protected
def find_resource
@@ -135,19 +150,6 @@ module Spree
@collection
end
def create_before
return if params[:product][:prototype_id].blank?
@prototype = Spree::Prototype.find(params[:product][:prototype_id])
end
def update_before
# We only reset the product properties if we're receiving a post from the form on that tab
return unless params[:clear_product_properties]
params[:product] ||= {}
end
def product_includes
[{ variants: [:images, { option_values: :option_type }] },
{ master: [:images, :default_price] }]

View File

@@ -4,14 +4,46 @@ module Spree
module Admin
class VariantsController < ResourceController
helper 'spree/products'
belongs_to 'spree/product', find_by: :permalink
new_action.before :new_before
def index
@url_filters = ::ProductFilters.new.extract(request.query_parameters)
end
def edit
@url_filters = ::ProductFilters.new.extract(request.query_parameters)
end
def update
@url_filters = ::ProductFilters.new.extract(request.query_parameters)
if @object.update(permitted_resource_params)
flash[:success] = flash_message_for(@object, :successfully_updated)
redirect_to admin_product_variants_url(params[:product_id], @url_filters)
else
redirect_to edit_admin_product_variant_url(params[:product_id], @object, @url_filters)
end
end
def new
@url_filters = ::ProductFilters.new.extract(request.query_parameters)
end
def create
@url_filters = ::ProductFilters.new.extract(request.query_parameters)
on_demand = params[:variant].delete(:on_demand)
on_hand = params[:variant].delete(:on_hand)
super
@object.attributes = permitted_resource_params
if @object.save
flash[:success] = flash_message_for(@object, :successfully_created)
redirect_to admin_product_variants_url(params[:product_id], @url_filters)
else
redirect_to new_admin_product_variant_url(params[:product_id], @url_filters)
end
return unless @object.present? && @object.valid?
@@ -26,6 +58,8 @@ module Spree
end
def destroy
@url_filters = ::ProductFilters.new.extract(request.query_parameters)
@variant = Spree::Variant.find(params[:id])
flash[:success] = if VariantDeleter.new.delete(@variant)
Spree.t('notice_messages.variant_deleted')
@@ -33,9 +67,7 @@ module Spree
Spree.t('notice_messages.variant_not_deleted')
end
respond_with(@variant) do |format|
format.html { redirect_to admin_product_variants_url(params[:product_id]) }
end
redirect_to admin_product_variants_url(params[:product_id], @url_filters)
end
protected

View File

@@ -12,10 +12,7 @@ class Enterprise < ActiveRecord::Base
preference :shopfront_order_cycle_order, :string, default: "orders_close_at"
preference :show_customer_names_to_suppliers, :boolean, default: false
# This is hopefully a temporary measure, pending the arrival of multiple named inventories
# for shops. We need this here to allow hubs to restrict visible variants to only those in
# their inventory if they so choose
# TODO: delegate this to a separate model instead of abusing Preferences.
# Allow hubs to restrict visible variants to only those in their inventory
preference :product_selection_from_inventory_only, :boolean, default: false
has_paper_trail only: [:owner_id, :sells], on: [:update]

View File

@@ -194,8 +194,6 @@ Spree::Order.class_eval do
end
# After changing line items of a completed order
# TODO: perhaps this should be triggered from a controller
# rather than an after_save callback?
def update_shipping_fees!
shipments.each do |shipment|
next if shipment.shipped?
@@ -222,8 +220,6 @@ Spree::Order.class_eval do
end
# After changing line items of a completed order
# TODO: perhaps this should be triggered from a controller
# rather than an after_save callback?
def update_payment_fees!
payments.each do |payment|
next if payment.completed?

View File

@@ -0,0 +1,11 @@
# frozen_string_literal: true
class ProductFilters
PRODUCT_FILTERS = [
'query', 'producerFilter', 'categoryFilter', 'sorting', 'importDateFilter'
].freeze
def extract(params)
params.select { |key, _value| PRODUCT_FILTERS.include?(key) }
end
end

View File

@@ -13,8 +13,6 @@
"ng-class" => "{valid: payment.$valid, open: accordion.payment}"}
= render 'checkout/accordion_heading'
-# TODO render this in Angular instead of server-side
-# The problem being how to render the partials
.row
.small-12.medium-12.large-6.columns
- available_payment_methods.each do |method|

View File

@@ -2,7 +2,6 @@
.row
.small-12.text-center.columns
%h1
/ TODO: Rohan - logo asset & width is content manageable:
%img{src: image_path("logo-white-notext.png"), title: Spree::Config.site_name}
%br/
%a.button.transparent{href: "/shops"}

View File

@@ -3,9 +3,9 @@
= render partial: 'spree/shared/error_messages', locals: { target: @image }
- content_for :page_actions do
%li= button_link_to t('spree.back_to_images_list'), admin_product_images_url(@product), icon: 'icon-arrow-left'
%li= button_link_to t('spree.back_to_images_list'), admin_product_images_url(@product, @url_filters), icon: 'icon-arrow-left'
= form_for [:admin, @product, @image], html: { multipart: true } do |f|
= form_for [:admin, @product, @image], url: admin_product_image_path(@product, @image, @url_filters), html: { multipart: true } do |f|
%fieldset
%legend{align: "center"}= @image.attachment_file_name
.field.alpha.three.columns.align-center
@@ -18,4 +18,4 @@
.form-buttons.filter-actions.actions
= button t('spree.actions.update'), 'icon-refresh'
%span.or= t('spree.or')
= link_to t('spree.actions.cancel'), admin_product_images_url(@product), id: 'cancel_link', class: 'button icon-remove'
= link_to t('spree.actions.cancel'), admin_product_images_url(@product, @url_filters), id: 'cancel_link', class: 'button icon-remove'

View File

@@ -2,7 +2,7 @@
= render partial: 'spree/admin/shared/product_tabs', locals: { current: 'Images'}
- content_for :page_actions do
%li= link_to_with_icon('icon-plus', t('spree.new_image'), new_admin_product_image_url(@product), id: 'new_image_link', class: 'button')
%li= link_to_with_icon('icon-plus', t('spree.new_image'), new_admin_product_image_url(@product, @url_filters), id: 'new_image_link', class: 'button')
#images
@@ -11,7 +11,7 @@
= t('spree.no_images_found')
\.
- else
%table.index.sortable{ "data-sortable-link" => "#{update_positions_admin_product_images_url(@product)}" }
%table.index.sortable{ "data-sortable-link" => "#{update_positions_admin_product_images_url(@product, @url_filters)}" }
%colgroup
%col{ style: "width: 5%" }/
%col{ style: "width: 10%" }/
@@ -36,5 +36,5 @@
%td= options_text_for(image)
%td= image.alt
%td.actions
= link_to_with_icon 'icon-edit', t('spree.edit'), edit_admin_product_image_url(@product, image), no_text: true, data: { action: 'edit'}
= link_to_delete image, { url: admin_product_image_url(@product, image), no_text: true }
= link_to_with_icon 'icon-edit', t('spree.edit'), edit_admin_product_image_url(@product, image, @url_filters), no_text: true, data: { action: 'edit'}
= link_to_delete image, { url: admin_product_image_url(@product, image, @url_filters), no_text: true }

View File

@@ -1,10 +1,10 @@
= form_for [:admin, @product, @image], html: { multipart: true } do |f|
= form_for [:admin, @product, @image], url: admin_product_images_path(@product, @image, @url_filters), html: { multipart: true } do |f|
%fieldset
%legend{ align: "center" }= t('spree.new_image')
= render partial: 'form', locals: { f: f }
.form-buttons.filter-actions.actions
= button t('spree.actions.update'), 'icon-refresh'
%span.or= t('spree.or')
= link_to_with_icon 'icon-remove', t('spree.actions.cancel'), admin_product_images_url(@product), id: 'cancel_link', class: 'button'
= link_to_with_icon 'icon-remove', t('spree.actions.cancel'), admin_product_images_url(@product, @url_filters), id: 'cancel_link', class: 'button'
= javascript_include_tag 'admin/spree/images/new.js'

View File

@@ -11,4 +11,4 @@
= f.text_field :value, class: 'autocomplete'
%td.actions
- if f.object.persisted?
= link_to_delete f.object, no_text: true
= link_to_delete f.object, { url: admin_product_product_property_url(@product, f.object, @url_filters), no_text: true }

View File

@@ -9,7 +9,7 @@
%li
= link_to_add_fields t('.add_product_properties'), 'tbody#product_properties', class: 'icon-plus button'
= form_for @product, url: admin_product_url(@product), method: :put do |f|
= form_for @product, url: admin_product_url(@product, @url_filters), method: :put do |f|
%fieldset.no-border-top
.add_product_properties
= image_tag 'select2-spinner.gif', plugin: 'spree', style: 'display:none;', id: 'busy_indicator'
@@ -46,7 +46,10 @@
%td.actions
= render partial: 'spree/admin/shared/edit_resource_links'
.form-buttons.filter-actions.actions
= button t('spree.actions.update'), 'icon-refresh'
%span.or= t('spree.or')
= link_to t('spree.actions.cancel'), admin_product_product_properties_url(@product, @url_filters), id: 'cancel_link', class: 'button icon-remove'
= hidden_field_tag 'clear_product_properties', 'true'

View File

@@ -1,5 +1,5 @@
- content_for :page_actions do
%li= button_link_to t('admin.products.back_to_products_list'), admin_products_path, :icon => 'icon-arrow-left'
%li= button_link_to t('admin.products.back_to_products_list'), "#{admin_products_path}#{(@url_filters.empty? ? "" : "#?#{@url_filters.to_query}")}", :icon => 'icon-arrow-left'
%li#new_product_link
= button_link_to t(:new_product), new_object_url, { :icon => 'icon-plus', :id => 'admin_new_product' }
@@ -8,7 +8,10 @@
= render :partial => 'spree/admin/shared/product_tabs', :locals => { :current => 'Product Details' }
= render :partial => 'spree/shared/error_messages', :locals => { :target => @product }
= form_for [:admin, @product], :method => :put, :html => { :multipart => true } do |f|
= form_for [:admin, @product], :url => admin_product_path(@product, @url_filters), :method => :put, :html => { :multipart => true } do |f|
%fieldset.no-border-top{'ng-app' => 'admin.products'}
= render :partial => 'form', :locals => { :f => f }
= render :partial => 'spree/admin/shared/edit_resource_links'
.form-buttons.filter-actions.actions
= button t(:update), 'icon-refresh'
%span.or= t(:or)
= button_link_to t(:cancel), "#{collection_url}#{(@url_filters.empty? ? "" : "#?#{@url_filters.to_query}")}", icon: 'icon-remove'

View File

@@ -1,8 +1,11 @@
= render partial: 'spree/admin/shared/product_sub_menu'
= render :partial => 'spree/admin/shared/product_tabs', :locals => { :current => 'Group Buy Options' }
= render :partial => 'spree/shared/error_messages', :locals => { :target => @product }
= render partial: 'spree/admin/shared/product_tabs', locals: { :current => 'Group Buy Options' }
= render partial: 'spree/shared/error_messages', locals: { :target => @product }
= form_for [:admin, @product], :method => :put, :html => { :multipart => true } do |f|
= form_for [:admin, @product], url: admin_product_url(@product, @url_filters), method: :put, html: { :multipart => true } do |f|
%fieldset.no-border-top
= render :partial => 'group_buy_form', :locals => { :f => f }
= render :partial => 'spree/admin/shared/edit_resource_links'
= render partial: 'group_buy_form', locals: { f: f }
.form-buttons.filter-actions.actions
= button t('spree.actions.update'), 'icon-refresh'
%span.or= t('spree.or')
= link_to t('spree.actions.cancel'), edit_admin_product_url(@product, @url_filters), id: 'cancel_link', class: 'button icon-remove'

View File

@@ -5,22 +5,20 @@
.quick_search.three.columns.alpha
%label{ for: 'quick_filter' }
%br
%input.quick-search.fullwidth{ ng: {model: 'query'}, name: "quick_filter", type: 'text', placeholder: t('admin.quick_search'), "ng-keypress" => "$event.keyCode === 13 && fetchProducts()" }
%input.quick-search.fullwidth{ ng: {model: 'q.query'}, name: "quick_filter", type: 'text', placeholder: t('admin.quick_search'), "ng-keypress" => "$event.keyCode === 13 && fetchProducts()" }
.one.columns &nbsp;
.filter_select.three.columns
%label{ for: 'producer_filter' }= t 'producer'
%br
%select.fullwidth{ id: 'producer_filter', 'ofn-select2-min-search' => 5, ng: {model: 'producerFilter', options: 'producer.id as producer.name for producer in producers'} }
%select.fullwidth{ id: 'producer_filter', 'ofn-select2-min-search' => 5, ng: {model: 'q.producerFilter', options: 'producer.id as producer.name for producer in producers'} }
.filter_select.three.columns
%label{ for: 'category_filter' }= t 'category'
%br
%select.fullwidth{ id: 'category_filter', 'ofn-select2-min-search' => 5, ng: {model: 'categoryFilter', options: 'taxon.id as taxon.name for taxon in taxons'} }
%select.fullwidth{ id: 'category_filter', 'ofn-select2-min-search' => 5, ng: {model: 'q.categoryFilter', options: 'taxon.id as taxon.name for taxon in taxons'} }
.filter_select.three.columns
%label{ for: 'import_filter' } Import Date
%br
%select.fullwidth{ id: 'import_date_filter', 'ofn-select2-min-search' => 5, ng: {model: 'importDateFilter', init: "importDates = #{@import_dates}; showLatestImport = #{@show_latest_import}"}}
%option{value: "{{date.id}}", ng: {repeat: "date in importDates" }}
{{date.name}}
%select.fullwidth{ id: 'import_date_filter', 'ofn-select2-min-search' => 5, ng: {model: 'q.importDateFilter', init: "importDates = #{@import_dates}; showLatestImport = #{@show_latest_import}", options: 'date.id as date.name for date in importDates'} }
.filter_clear.three.columns.omega
%label{ for: 'clear_all_filters' }

View File

@@ -3,12 +3,12 @@
%img.spinner{ src: image_path("spinning-circles.svg") }
%h1= t('.title')
%div.sixteen.columns.alpha{ 'ng-show' => '!RequestMonitor.loading && products.length == 0 && query.length==0' }
%div.sixteen.columns.alpha{ 'ng-show' => '!RequestMonitor.loading && products.length == 0 && q.query.length == 0' }
%h1#no_results= t('.no_products')
%div.sixteen.columns.alpha{ 'ng-show' => '!RequestMonitor.loading && products.length == 0 && query.length!=0' }
%div.sixteen.columns.alpha{ 'ng-show' => '!RequestMonitor.loading && products.length == 0 && q.query.length != 0' }
%h1#no_results
= t('.no_results')
'
{{query}}
'
{{q.query}}
'

View File

@@ -1,9 +1,12 @@
= render partial: 'spree/admin/shared/product_sub_menu'
= render :partial => 'spree/admin/shared/product_tabs', :locals => { :current => t(:search) }
= render :partial => 'spree/shared/error_messages', :locals => { :target => @product }
= render partial: 'spree/admin/shared/product_tabs', locals: { current: t(:search) }
= render partial: 'spree/shared/error_messages', locals: { target: @product }
%div{ 'ng-app' => 'ofn.admin' }
= form_for [:admin, @product], :method => :put, :html => { :multipart => true } do |f|
= form_for [:admin, @product], url: admin_product_url(@product, @url_filters), method: :put, html: { :multipart => true } do |f|
%fieldset.no-border-top
= render :partial => 'seo_form', :locals => { :f => f }
= render :partial => 'spree/admin/shared/edit_resource_links'
.form-buttons.filter-actions.actions
= button t('spree.actions.update'), 'icon-refresh'
%span.or= t('spree.or')
= link_to t('spree.actions.cancel'), edit_admin_product_url(@product, @url_filters), id: 'cancel_link', class: 'button icon-remove'

View File

@@ -12,22 +12,22 @@
- if can?(:admin, Spree::Product)
- klass = current == 'Product Details' ? 'active' : ''
%li{:class => klass}
= link_to_with_icon 'icon-edit', t('admin.products.tabs.product_details'), edit_admin_product_url(@product)
= link_to_with_icon 'icon-edit', t('admin.products.tabs.product_details'), edit_admin_product_url(@product, @url_filters)
- if can?(:admin, Spree::Image)
- klass = current == 'Images' ? 'active' : ''
%li{:class => klass}
= link_to_with_icon 'icon-picture', t('admin.products.tabs.images'), admin_product_images_url(@product)
= link_to_with_icon 'icon-picture', t('admin.products.tabs.images'), admin_product_images_url(@product, @url_filters)
- if can?(:admin, Spree::Variant)
- klass = current == 'Variants' ? 'active' : ''
%li{:class => klass}
= link_to_with_icon 'icon-th-large', t('admin.products.tabs.variants'), admin_product_variants_url(@product)
= link_to_with_icon 'icon-th-large', t('admin.products.tabs.variants'), admin_product_variants_url(@product, @url_filters)
- if can?(:admin, Spree::ProductProperty)
- klass = current == 'Product Properties' ? 'active' : ''
%li{:class => klass}
= link_to_with_icon 'icon-tasks', t('admin.products.tabs.product_properties'), admin_product_product_properties_url(@product)
= link_to_with_icon 'icon-tasks', t('admin.products.tabs.product_properties'), admin_product_product_properties_url(@product, @url_filters)
- klass = current == 'Group Buy Options' ? 'active' : ''
%li{:class => klass}
= link_to_with_icon 'icon-tasks', t('admin.products.tabs.group_buy_options'), group_buy_options_admin_product_url(@product)
= link_to_with_icon 'icon-tasks', t('admin.products.tabs.group_buy_options'), group_buy_options_admin_product_url(@product, @url_filters)
- klass = current == t(:search) ? 'active' : ''
%li{:class => klass}
= link_to_with_icon 'icon-tasks', t(:search), seo_admin_product_url(@product)
= link_to_with_icon 'icon-tasks', t(:search), seo_admin_product_url(@product, @url_filters)

View File

@@ -4,8 +4,11 @@
= render partial: 'spree/shared/error_messages', locals: { target: @variant }
= form_for [:admin, @product, @variant] do |f|
= form_for [:admin, @product, @variant], :url => admin_product_variant_path(@product, @variant, @url_filters) do |f|
%fieldset.no-border-top
%div
= render partial: 'form', locals: { f: f }
= render partial: 'spree/admin/shared/edit_resource_links'
.form-buttons.filter-actions.actions
= button t(:update), 'icon-refresh'
%span.or= t(:or)
= button_link_to t(:cancel), collection_url(@url_filters), icon: 'icon-remove'

View File

@@ -27,8 +27,8 @@
%td.align-center= variant.display_price.to_html
%td.align-center= variant.sku
%td.actions
= link_to_edit(variant, no_text: true) unless variant.deleted?
= link_to_delete(variant, no_text: true) unless variant.deleted?
= link_to_with_icon('icon-edit', Spree.t(:edit), edit_object_url(variant, @url_filters), no_text: true) unless variant.deleted?
= link_to_delete(variant, { url: object_url(variant, @url_filters), no_text: true }) unless variant.deleted?
- if @product.empty_option_values?
%p.first_add_option_types.no-objects-found
@@ -41,6 +41,6 @@
- content_for :page_actions do
%ul.inline-menu
%li#new_var_link
= link_to_with_icon('icon-plus', t('.new_variant'), new_admin_product_variant_url(@product), class: 'button')
= link_to_with_icon('icon-plus', t('.new_variant'), new_admin_product_variant_url(@product, @url_filters), class: 'button')
%li= link_to_with_icon('icon-filter', @deleted.blank? ? t('.show_deleted') : t('.show_active'), admin_product_variants_url(@product, deleted: @deleted.blank? ? "on" : "off"), class: 'button')
%li= link_to_with_icon('icon-filter', @deleted.blank? ? t('.show_deleted') : t('.show_active'), admin_product_variants_url(@product, @url_filters.merge(deleted: @deleted.blank? ? "on" : "off")), class: 'button')

View File

@@ -1,7 +1,11 @@
= render partial: 'spree/shared/error_messages', locals: { target: @variant }
= form_for [:admin, @product, @variant] do |f|
= form_for [:admin, @product, @variant], :url => admin_product_variants_path(@product, @url_filters) do |f|
%fieldset{'data-hook' => "admin_variant_new_form"}
%legend{align: "center"}= t('.new_variant')
= render partial: 'form', locals: { f: f }
= render partial: 'spree/admin/shared/new_resource_links'
.form-buttons.filter-actions.actions
= button t('actions.create'), 'icon-ok'
%span.or= t(:or)
= button_link_to t('actions.cancel'), collection_url(@url_filters), icon: 'icon-remove'

View File

@@ -514,37 +514,61 @@ feature '
login_as_admin_and_visit spree.admin_products_path
end
it "shows an edit button for products, which takes the user to the standard edit page for that product in a new window" do
it "shows an edit button for products, which takes the user to the standard edit page for that product" do
expect(page).to have_selector "a.edit-product", count: 2
new_window = window_opened_by do
within "tr#p_#{p1.id}" do
find("a.edit-product").click
end
within "tr#p_#{p1.id}" do
find("a.edit-product").click
end
within_window new_window do
expect(URI.parse(current_url).path).to eq "/admin/products/#{p1.permalink}/edit"
page.execute_script('window.close()')
end
expect(URI.parse(current_url).path).to eq spree.edit_admin_product_path(v1.product.permalink)
end
it "shows an edit button for variants, which takes the user to the standard edit page for that variant in a new window" do
it "shows an edit button for products, which takes the user to the standard edit page for that product, url includes selected filter" do
expect(page).to have_selector "a.edit-product", count: 2
# Set a filter
select2_select p1.supplier.name, from: "producer_filter"
apply_filters
within "tr#p_#{p1.id}" do
find("a.edit-product").click
end
uri = URI.parse(current_url)
expect("#{uri.path}?#{uri.query}").to eq spree.edit_admin_product_path(v1.product.permalink, producerFilter: p1.supplier.id)
end
it "shows an edit button for variants, which takes the user to the standard edit page for that variant" do
expect(page).to have_selector "a.view-variants"
all("a.view-variants").each(&:click)
expect(page).to have_selector "a.edit-variant", count: 2
new_window = window_opened_by do
within "tr#v_#{v1.id}" do
find("a.edit-variant").click
end
within "tr#v_#{v1.id}" do
find("a.edit-variant").click
end
within_window new_window do
expect(URI.parse(current_url).path).to eq "/admin/products/#{v1.product.permalink}/variants/#{v1.id}/edit"
page.execute_script('window.close()')
uri = URI.parse(current_url)
expect(URI.parse(current_url).path).to eq spree.edit_admin_product_variant_path(v1.product.permalink, v1.id)
end
it "shows an edit button for variants, which takes the user to the standard edit page for that variant, url includes selected filter" do
expect(page).to have_selector "a.view-variants"
all("a.view-variants").each(&:click)
expect(page).to have_selector "a.edit-variant", count: 2
# Set a filter
select2_select p1.supplier.name, from: "producer_filter"
apply_filters
within "tr#v_#{v1.id}" do
find("a.edit-variant").click
end
uri = URI.parse(current_url)
expect("#{uri.path}?#{uri.query}").to eq spree.edit_admin_product_variant_path(v1.product.permalink, v1.id, producerFilter: p1.supplier.id)
end
end

View File

@@ -94,6 +94,7 @@ feature '
context "as an enterprise user" do
let!(:tax_category) { create(:tax_category) }
let(:filter) { { producerFilter: 2 } }
before do
@new_user = create(:user)
@@ -151,6 +152,42 @@ feature '
expect(product.tax_category).to eq(tax_category)
end
scenario "editing a product comming from the bulk product update page with filter" do
product = create(:simple_product, name: 'a product', supplier: @supplier2)
visit spree.edit_admin_product_path(product, filter)
click_button 'Update'
expect(flash_message).to eq('Product "a product" has been successfully updated!')
# Check the url still includes the filters
uri = URI.parse(current_url)
expect("#{uri.path}?#{uri.query}").to eq spree.edit_admin_product_path(product, filter)
# Link back to the bulk product update page should include the filters
expected_admin_product_url = Regexp.new(Regexp.escape("#{spree.admin_products_path}#?#{filter.to_query}"))
expect(page).to have_link(I18n.t('admin.products.back_to_products_list'), href: expected_admin_product_url)
expect(page).to have_link(I18n.t(:cancel), href: expected_admin_product_url)
expected_product_url = Regexp.new(Regexp.escape(spree.edit_admin_product_path(product.permalink, filter)))
expect(page).to have_link(I18n.t('admin.products.tabs.product_details'), href: expected_product_url)
expected_product_image_url = Regexp.new(Regexp.escape(spree.admin_product_images_path(product.permalink, filter)))
expect(page).to have_link(I18n.t('admin.products.tabs.images'), href: expected_product_image_url)
expected_product_variant_url = Regexp.new(Regexp.escape(spree.admin_product_variants_path(product.permalink, filter)))
expect(page).to have_link(I18n.t('admin.products.tabs.variants'), href: expected_product_variant_url)
expected_product_properties_url = Regexp.new(Regexp.escape(spree.admin_product_product_properties_path(product.permalink, filter)))
expect(page).to have_link(I18n.t('admin.products.tabs.product_properties'), href: expected_product_properties_url)
expected_product_group_buy_option_url = Regexp.new(Regexp.escape(spree.group_buy_options_admin_product_path(product.permalink, filter)))
expect(page).to have_link(I18n.t('admin.products.tabs.group_buy_options'), href: expected_product_group_buy_option_url)
expected_product_seo_url = Regexp.new(Regexp.escape(spree.seo_admin_product_path(product.permalink, filter)))
expect(page).to have_link(I18n.t(:search), href: expected_product_seo_url)
end
scenario "editing product group buy options" do
product = product = create(:simple_product, supplier: @supplier2)
@@ -167,8 +204,30 @@ feature '
expect(product.group_buy_unit_size).to eq(10.0)
end
scenario "editing product Search" do
scenario "loading editing product group buy options with url filters" do
product = product = create(:simple_product, supplier: @supplier2)
visit spree.group_buy_options_admin_product_path(product, filter)
expected_cancel_link = Regexp.new(Regexp.escape(spree.edit_admin_product_path(product, filter)))
expect(page).to have_link(I18n.t(:cancel), href: expected_cancel_link)
end
scenario "editing product group buy options with url filter" do
product = product = create(:simple_product, supplier: @supplier2)
visit spree.group_buy_options_admin_product_path(product, filter)
choose('product_group_buy_1')
fill_in 'Bulk unit size', with: '10'
click_button 'Update'
uri = URI.parse(current_url)
expect("#{uri.path}?#{uri.query}").to eq spree.edit_admin_product_path(product, filter)
end
scenario "editing product Search" do
product = create(:simple_product, supplier: @supplier2)
visit spree.edit_admin_product_path product
within('#sidebar') { click_link 'Search' }
fill_in 'Product Search Keywords', with: 'Product Search Keywords'
@@ -180,13 +239,48 @@ feature '
expect(product.meta_keywords).to eq('Product Search Keywords')
end
scenario "loading editing product Search with url filters" do
product = create(:simple_product, supplier: @supplier2)
visit spree.seo_admin_product_path(product, filter)
expected_cancel_link = Regexp.new(Regexp.escape(spree.edit_admin_product_path(product, filter)))
expect(page).to have_link(I18n.t(:cancel), href: expected_cancel_link)
end
scenario "editing product Search with url filter" do
product = create(:simple_product, supplier: @supplier2)
visit spree.seo_admin_product_path(product, filter)
fill_in 'Product Search Keywords', with: 'Product Search Keywords'
fill_in 'Notes', with: 'Just testing Notes'
click_button 'Update'
uri = URI.parse(current_url)
expect("#{uri.path}?#{uri.query}").to eq spree.edit_admin_product_path(product, filter)
end
scenario "loading product properties page including url filters", js: true do
product = create(:simple_product, supplier: @supplier2)
visit spree.admin_product_product_properties_path(product, filter)
uri = URI.parse(current_url)
# we stay on the same url as the new image content is loaded via an ajax call
expect("#{uri.path}?#{uri.query}").to eq spree.admin_product_product_properties_path(product, filter)
expected_cancel_link = Regexp.new(Regexp.escape(spree.admin_product_product_properties_path(product, filter)))
expect(page).to have_link(I18n.t(:cancel), href: expected_cancel_link)
end
scenario "deleting product properties", js: true do
# Given a product with a property
p = create(:simple_product, supplier: @supplier2)
p.set_property('fooprop', 'fooval')
product = create(:simple_product, supplier: @supplier2)
product.set_property('fooprop', 'fooval')
# When I navigate to the product properties page
visit spree.admin_product_product_properties_path(p)
visit spree.admin_product_product_properties_path(product)
expect(page).to have_select2 'product_product_properties_attributes_0_property_name', selected: 'fooprop'
expect(page).to have_field 'product_product_properties_attributes_0_value', with: 'fooval'
@@ -199,10 +293,45 @@ feature '
# Then the property should have been deleted
expect(page).not_to have_field 'product_product_properties_attributes_0_property_name', with: 'fooprop'
expect(page).not_to have_field 'product_product_properties_attributes_0_value', with: 'fooval'
expect(p.reload.property('fooprop')).to be_nil
expect(product.reload.property('fooprop')).to be_nil
end
scenario "loading new image page", js: true do
scenario "deleting product properties including url filters", js: true do
# Given a product with a property
product = create(:simple_product, supplier: @supplier2)
product.set_property('fooprop', 'fooval')
# When I navigate to the product properties page
visit spree.admin_product_product_properties_path(product, filter)
# And I delete the property
accept_alert do
page.all('a.delete-resource').first.click
end
uri = URI.parse(current_url)
expect("#{uri.path}?#{uri.query}").to eq spree.admin_product_product_properties_path(product, filter)
end
scenario "adding product properties including url filters", js: true do
# Given a product
product = create(:simple_product, supplier: @supplier2)
product.set_property('fooprop', 'fooval')
# When I navigate to the product properties page
visit spree.admin_product_product_properties_path(product, filter)
# And I add a property
select 'fooprop', from: 'product_product_properties_attributes_0_property_name'
fill_in 'product_product_properties_attributes_0_value', with: 'fooval2'
click_button 'Update'
uri = URI.parse(current_url)
expect("#{uri.path}?#{uri.query}").to eq spree.edit_admin_product_path(product, filter)
end
scenario "loading new product image page", js: true do
product = create(:simple_product, supplier: @supplier2)
visit spree.admin_product_images_path(product)
@@ -212,6 +341,80 @@ feature '
expect(page).to have_selector "#image_attachment"
end
scenario "loading new product image page including url filters", js: true do
product = create(:simple_product, supplier: @supplier2)
visit spree.admin_product_images_path(product, filter)
page.find('a#new_image_link').click
uri = URI.parse(current_url)
# we stay on the same url as the new image content is loaded via an ajax call
expect("#{uri.path}?#{uri.query}").to eq spree.admin_product_images_path(product, filter)
expected_cancel_link = Regexp.new(Regexp.escape(spree.admin_product_images_path(product, filter)))
expect(page).to have_link(I18n.t(:cancel), href: expected_cancel_link)
end
scenario "upload a new product image including url filters", js: true do
file_path = Rails.root + "spec/support/fixtures/thinking-cat.jpg"
product = create(:simple_product, supplier: @supplier2)
visit spree.admin_product_images_path(product, filter)
page.find('a#new_image_link').click
attach_file('image_attachment', file_path)
click_button "Update"
uri = URI.parse(current_url)
expect("#{uri.path}?#{uri.query}").to eq spree.admin_product_images_path(product, filter)
end
scenario "loading image page including url filter", js: true do
product = create(:simple_product, supplier: @supplier2)
visit spree.admin_product_images_path(product, filter)
expected_new_image_link = Regexp.new(Regexp.escape(spree.new_admin_product_image_path(product, filter)))
expect(page).to have_link(I18n.t('spree.new_image'), href: expected_new_image_link)
end
scenario "loading edit product image page including url filter", js: true do
product = create(:simple_product, supplier: @supplier2)
image = File.open(File.expand_path('../../../app/assets/images/logo-white.png', __dir__))
image_object = Spree::Image.create(viewable_id: product.master.id, viewable_type: 'Spree::Variant', alt: "position 1", attachment: image, position: 1)
visit spree.admin_product_images_path(product, filter)
page.find("a.icon-edit").click
uri = URI.parse(current_url)
expect("#{uri.path}?#{uri.query}").to eq spree.edit_admin_product_image_path(product, image_object, filter)
expected_cancel_link = Regexp.new(Regexp.escape(spree.admin_product_images_path(product, filter)))
expect(page).to have_link(I18n.t(:cancel), href: expected_cancel_link)
expect(page).to have_link("Back To Images List", href: expected_cancel_link)
end
scenario "updating a product image including url filter", js: true do
product = create(:simple_product, supplier: @supplier2)
image = File.open(File.expand_path('../../../app/assets/images/logo-white.png', __dir__))
image_object = Spree::Image.create(viewable_id: product.master.id, viewable_type: 'Spree::Variant', alt: "position 1", attachment: image, position: 1)
file_path = Rails.root + "spec/support/fixtures/thinking-cat.jpg"
visit spree.admin_product_images_path(product, filter)
page.find("a.icon-edit").click
attach_file('image_attachment', file_path)
click_button "Update"
uri = URI.parse(current_url)
expect("#{uri.path}?#{uri.query}").to eq spree.admin_product_images_path(product, filter)
end
scenario "deleting product images", js: true do
product = create(:simple_product, supplier: @supplier2)
image = File.open(File.expand_path('../../../app/assets/images/logo-white.png', __dir__))
@@ -228,5 +431,20 @@ feature '
expect(page).to_not have_selector "table.index td img"
expect(product.reload.images.count).to eq 0
end
scenario "deleting product image including url filter", js: true do
product = create(:simple_product, supplier: @supplier2)
image = File.open(File.expand_path('../../../app/assets/images/logo-white.png', __dir__))
Spree::Image.create(viewable_id: product.master.id, viewable_type: 'Spree::Variant', alt: "position 1", attachment: image, position: 1)
visit spree.admin_product_images_path(product, filter)
accept_alert do
page.find('a.delete-resource').click
end
uri = URI.parse(current_url)
expect("#{uri.path}?#{uri.query}").to eq spree.admin_product_images_path(product, filter)
end
end
end

View File

@@ -7,23 +7,97 @@ feature '
include AuthenticationHelper
include WebHelper
scenario "creating a new variant" do
# Given a product with a unit-related option type
product = create(:simple_product, variant_unit: "weight", variant_unit_scale: "1")
describe "new variant", js: true do
scenario "creating a new variant" do
# Given a product with a unit-related option type
product = create(:simple_product, variant_unit: "weight", variant_unit_scale: "1")
# When I create a variant on the product
login_as_admin_and_visit spree.admin_product_variants_path product
click_link 'New Variant'
fill_in 'unit_value_human', with: '1'
fill_in 'variant_unit_description', with: 'foo'
click_button 'Create'
# When I create a variant on the product
login_as_admin_and_visit spree.admin_product_variants_path product
click_link 'New Variant'
# Then the variant should have been created
expect(page).to have_content "Variant \"#{product.name}\" has been successfully created!"
fill_in 'unit_value_human', with: '1'
fill_in 'variant_unit_description', with: 'foo'
click_button 'Create'
# Then the variant should have been created
expect(page).to have_content "Variant \"#{product.name}\" has been successfully created!"
end
scenario "creating a new variant from product variant page with filter" do
# Given a product with a unit-related option type
product = create(:simple_product, variant_unit: "weight", variant_unit_scale: "1")
filter = { producerFilter: 2 }
# When I create a variant on the product
login_as_admin_and_visit spree.admin_product_variants_path(product, filter)
click_link 'New Variant'
uri = URI.parse(current_url)
expect("#{uri.path}?#{uri.query}").to eq spree.new_admin_product_variant_path(product, filter)
# Cancel link should include product filter
expected_cancel_url = Regexp.new(
Regexp.escape(spree.admin_product_variants_path(product, filter))
)
expect(page).to have_link(I18n.t('actions.cancel'), href: expected_cancel_url)
end
end
describe "viewing product variant" do
scenario "when the product page has a product filter" do
# Given a product with a unit-related option type
product = create(:simple_product, variant_unit: "weight", variant_unit_scale: "1")
filter = { producerFilter: 2 }
# When I create a variant on the product
login_as_admin_and_visit spree.admin_product_variants_path(product, filter)
visit spree.admin_product_variants_path(product, filter)
expected_new_url = Regexp.new(
Regexp.escape(spree.new_admin_product_variant_path(product, filter))
)
expect(page).to have_link("New Variant", href: expected_new_url)
expected_show_delete_url = Regexp.new(
Regexp.escape(spree.admin_product_variants_path(product, { deleted: 'on' }.merge(filter)))
)
expect(page).to have_link("Show Deleted", href: expected_show_delete_url)
# Variant link should include product filter
variant = product.variants.first
expected_edit_url = Regexp.new(
Regexp.escape(spree.edit_admin_product_variant_path(product, variant, filter))
)
expect(page).to have_link(I18n.t(:edit), href: expected_edit_url)
expected_delete_url = Regexp.new(
Regexp.escape(spree.admin_product_variant_path(product, variant, filter))
)
expect(page).to have_link(I18n.t(:delete), href: expected_delete_url)
end
end
describe "editing unit value and description for a variant", js: true do
scenario "when the product variant page has product filter" do
product = create(:simple_product, variant_unit: "weight", variant_unit_scale: "1")
filter = { producerFilter: 2 }
# When I create a variant on the product
login_as_admin_and_visit spree.admin_product_variants_path(product, filter)
page.find('table.index .icon-edit').click
# Cancel link should include product filter
expected_cancel_url = Regexp.new(
Regexp.escape(spree.admin_product_variants_path(product, filter))
)
expect(page).to have_link(I18n.t('actions.cancel'), href: expected_cancel_url)
end
scenario "when variant_unit is weight" do
# Given a product with unit-related option types, with a variant
product = create(:simple_product, variant_unit: "weight", variant_unit_scale: "1")

View File

@@ -246,7 +246,7 @@ describe "filtering products for submission to database", ->
]
describe "AdminProductEditCtrl", ->
$ctrl = $scope = $timeout = $httpBackend = BulkProducts = DirtyProducts = DisplayProperties = null
$ctrl = $scope = $timeout = $httpBackend = BulkProducts = DirtyProducts = DisplayProperties = ProductFiltersUrl = windowStub = null
beforeEach ->
module "ofn.admin"
@@ -258,7 +258,7 @@ describe "AdminProductEditCtrl", ->
$provide.value 'columns', []
null
beforeEach inject((_$controller_, _$timeout_, $rootScope, _$httpBackend_, _BulkProducts_, _DirtyProducts_, _DisplayProperties_) ->
beforeEach inject((_$controller_, _$timeout_, $rootScope, _$httpBackend_, _BulkProducts_, _DirtyProducts_, _DisplayProperties_, _ProductFiltersUrl_) ->
$scope = $rootScope.$new()
$ctrl = _$controller_
$timeout = _$timeout_
@@ -266,16 +266,38 @@ describe "AdminProductEditCtrl", ->
BulkProducts = _BulkProducts_
DirtyProducts = _DirtyProducts_
DisplayProperties = _DisplayProperties_
ProductFiltersUrl = _ProductFiltersUrl_
$ctrl "AdminProductEditCtrl", {$scope: $scope, $timeout: $timeout}
# Stub the window object so we don't get redirected when href is updated
windowStub = {navigator: {userAgent: 'foo'}, location: {href: ''}}
$ctrl "AdminProductEditCtrl", {$scope: $scope, $timeout: $timeout, $window: windowStub}
)
describe "loading data upon initialisation", ->
it "gets a list of producers and then resets products with a list of data", ->
beforeEach ->
spyOn($scope, "fetchProducts").and.returnValue "nothing"
it "gets a list of producers and then resets products with a list of data", ->
$scope.initialise()
expect($scope.fetchProducts.calls.count()).toBe 1
it "gets a list of products applying filters from the url", inject ($location) ->
query = 'lala'
producerFilter = 2
categoryFilter = 5
sorting = 'name desc'
importDateFilter = '2020-06-08'
$location.search({query: query, producerFilter: producerFilter, categoryFilter: categoryFilter, sorting: sorting, importDateFilter: importDateFilter})
$scope.initialise()
expect($scope.q.query).toBe query
expect($scope.q.producerFilter).toBe producerFilter
expect($scope.q.categoryFilter).toBe categoryFilter
expect($scope.q.sorting).toBe sorting
expect($scope.q.importDateFilter).toBe importDateFilter
describe "fetching products", ->
$q = null
deferred = null
@@ -295,6 +317,28 @@ describe "AdminProductEditCtrl", ->
$scope.$digest()
expect($scope.resetProducts).toHaveBeenCalled()
it "updates url with filter after data has been received", inject ($location, $window) ->
query = 'lala'
producerFilter = 2
categoryFilter = 5
sorting = 'name desc'
importDateFilter = '2020-06-08'
$scope.q.query = query
$scope.q.producerFilter = producerFilter
$scope.q.categoryFilter = categoryFilter
$scope.q.sorting = sorting
$scope.q.importDateFilter = importDateFilter
$scope.fetchProducts()
$scope.$digest()
encodedSorting = $window.encodeURIComponent(sorting)
encodedDate = $window.encodeURIComponent(importDateFilter)
expect($location.url()).toBe(
"?producerFilter=#{producerFilter}&categoryFilter=#{categoryFilter}&query=#{query}&sorting=#{encodedSorting}&importDateFilter=#{encodedDate}"
)
describe "resetting products", ->
beforeEach ->
spyOn DirtyProducts, "clear"
@@ -893,18 +937,87 @@ describe "AdminProductEditCtrl", ->
id: 13
name: "P1"
describe "editWarn", ->
testProduct = testVariant = null
beforeEach ->
available_on = new Date()
testProduct =
id: 1
name: "TestProduct"
description: ""
available_on: available_on
deleted_at: null
permalink: 'test-product'
permalink_live: 'test-product'
meta_description: null
meta_keywords: null
tax_category_id: null
shipping_category_id: null
created_at: null
updated_at: null
on_hand: 0
on_demand: false
producer_id: 5
group_buy: null
group_buy_unit_size: null
master:
id: 2
unit_value: 250
unit_description: "foo"
describe 'product has variant', ->
it 'should load the edit product variant page', ->
testVariant =
id: 2
name: "TestVariant"
$scope.editWarn(testProduct, testVariant)
expect(windowStub.location.href).toBe(
"/admin/products/#{testProduct.permalink_live}/variants/#{testVariant.id}/edit"
)
describe 'product has no variant', ->
it 'should display unsaved changes confirmation if there are any DirtyProduct', inject ($window, DirtyProducts) ->
spyOn($window, 'confirm')
spyOn(DirtyProducts, 'count').and.returnValue 2
$scope.editWarn(testProduct, null)
expect($window.confirm).toHaveBeenCalled()
it 'should load the edit product page', inject ->
$scope.editWarn(testProduct, null)
expect(windowStub.location.href).toBe(
"/admin/products/#{testProduct.permalink_live}/edit"
)
it 'should load edit product page including the selected filters', inject ($httpParamSerializer) ->
query = 'lala'
category = 3
$scope.q.query = query
$scope.q.categoryFilter = category
# use $httpParamSerializer as it will sort parameters alphabetically
expectedFilter = $httpParamSerializer({ query: query, categoryFilter: category })
$scope.editWarn(testProduct, null)
expect(windowStub.location.href).toBe(
"/admin/products/#{testProduct.permalink_live}/edit?#{expectedFilter}"
)
describe "filtering products", ->
describe "clearing filters", ->
it "resets filter variables", ->
$scope.query = "lala"
$scope.producerFilter = "5"
$scope.categoryFilter = "6"
$scope.q.query = "lala"
$scope.q.producerFilter = "5"
$scope.q.categoryFilter = "6"
$scope.resetSelectFilters()
expect($scope.query).toBe ""
expect($scope.producerFilter).toBeUndefined
expect($scope.categoryFilter).toBeUndefined
expect($scope.q.query).toBe ""
expect($scope.q.producerFilter).toBeUndefined
expect($scope.q.categoryFilter).toBeUndefined
describe "converting arrays of objects with ids to an object with ids as keys", ->

View File

@@ -0,0 +1,68 @@
describe "ProductFiltersUrl service", ->
ProductFiltersUrl = null
beforeEach ->
module "ofn.admin"
beforeEach inject (_ProductFiltersUrl_) ->
ProductFiltersUrl = _ProductFiltersUrl_
describe "loadFromUrl", ->
it "should return a hash with value populated for filters existing in parameter", ->
producerFilter = 2
query = 'fruit'
filters = ProductFiltersUrl.loadFromUrl(producerFilter: producerFilter, query: query)
expect(filters.producerFilter).toBe producerFilter
expect(filters.query).toBe query
it "should return a hash with empty value for filters missing from parameter", ->
filters = ProductFiltersUrl.loadFromUrl({})
expect(filters.producerFilter).toBe ""
expect(filters.query).toBe ""
expect(filters.categoryFilter).toBe ""
expect(filters.sorting).toBe ""
expect(filters.importDateFilter).toBe ""
describe "generate", ->
it 'should filter given hash with productFilters', ->
producerFilter = 2
query = 'fruit'
filters = ProductFiltersUrl.generate(
producerFilter: producerFilter, query: query, otherParam: 'otherParam'
)
expect(filters.producerFilter).toBe producerFilter
expect(filters.query).toBe query
expect(filters.otherParam).toBe undefined
describe "buildUrl", ->
it 'should return a url adding filters to the baseUrl', inject ($httpParamSerializer) ->
query = 'lala'
producerFilter = 2
categoryFilter = 5
sorting = 'name desc'
importDateFilter = '2020-06-08'
filters = {
producerFilter: producerFilter
categoryFilter: categoryFilter
query: query
sorting: sorting
importDateFilter: importDateFilter
}
baseUrl = "openfoodnetwork.org.au"
url = ProductFiltersUrl.buildUrl(baseUrl, filters)
expectedFilters = $httpParamSerializer(filters)
expect(url).toBe("#{baseUrl}?#{expectedFilters}")
it 'should return baseUrl if filters are empty', ->
baseUrl = "openfoodnetwork.org.au"
url = ProductFiltersUrl.buildUrl(baseUrl, {})
expect(url).toBe baseUrl

View File

@@ -0,0 +1,17 @@
# frozen_string_literal: true
require 'spec_helper'
describe ProductFilters do
describe "extract" do
it "should return a hash including only key from ProductFilters::PRODUCT_FILTERS" do
params = { 'id' => 20, 'producerFilter' => 2, 'categoryFilter' => 5 }
filters = ProductFilters.new.extract(params)
expect(filters).not_to include 'id'
expect(filters).to include 'producerFilter'
expect(filters).to include 'categoryFilter'
end
end
end