diff --git a/app/assets/javascripts/admin/bulk_product_update.js.coffee b/app/assets/javascripts/admin/bulk_product_update.js.coffee index 94939f3f91..7571aaaaea 100644 --- a/app/assets/javascripts/admin/bulk_product_update.js.coffee +++ b/app/assets/javascripts/admin/bulk_product_update.js.coffee @@ -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, $httpParamSerializer, BulkProducts, DisplayProperties, DirtyProducts, VariantUnitManager, StatusMessage, producers, Taxons, Columns, tax_categories, RequestMonitor, SortOptions, ErrorsParser) -> $scope.StatusMessage = StatusMessage $scope.columns = Columns.columns @@ -13,34 +13,30 @@ 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 } + productFilters = ['producerFilter', 'categoryFilter', 'query', 'sorting', 'importDateFilter'] + $scope.producerFilter = "" + $scope.categoryFilter = "" + $scope.importDateFilter = "" + $scope.query = "" + $scope.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 + loadFilterFromUrl = -> + filters = $location.search() + for filter in productFilters + $scope[filter] = if filters[filter] then filters[filter] else "" + $scope.initialise = -> + loadFilterFromUrl() $scope.fetchProducts() $scope.$watchCollection '[query, producerFilter, categoryFilter, importDateFilter, per_page]', -> @@ -50,6 +46,13 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout $scope.page = newPage $scope.fetchProducts() + generateFilter = -> + filters = {} + for filter in productFilters + filters[filter] = $scope[filter] if $scope[filter] + + filters + $scope.fetchProducts = -> removeClearedValues() params = { @@ -62,6 +65,8 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout per_page: $scope.per_page } RequestMonitor.load(BulkProducts.fetch(params).$promise).then -> + # update url with the filters used + $location.search(generateFilter()) $scope.resetProducts() removeClearedValues = -> @@ -122,8 +127,9 @@ angular.module("ofn.admin").controller "AdminProductEditCtrl", ($scope, $timeout $scope.editWarn = (product, variant) -> if confirm_unsaved_changes() - window.open(editProductUrl(product, variant), "_blank") - + filterUrl = $httpParamSerializer(generateFilter()) + filterUrl = "?#{filterUrl}" if filterUrl isnt "" + $window.location.href = "#{editProductUrl(product, variant)}#{filterUrl}" $scope.toggleShowAllVariants = -> showVariants = !DisplayProperties.showVariants 0 diff --git a/app/assets/javascripts/admin/directives/select2_min_search.js.coffee b/app/assets/javascripts/admin/directives/select2_min_search.js.coffee index 1d55d886aa..0d9e9faa75 100644 --- a/app/assets/javascripts/admin/directives/select2_min_search.js.coffee +++ b/app/assets/javascripts/admin/directives/select2_min_search.js.coffee @@ -5,5 +5,10 @@ angular.module("ofn.admin").directive "ofnSelect2MinSearch", -> minimumResultsForSearch: attrs.ofnSelect2MinSearch ngModel.$formatters.push (value) -> - if (value) - element.select2('val', value); \ No newline at end of file + # select2 populate options with a value like "number:3" or "string:category" but + # select2('val', value) doesn't do the translation for us as one would expect + if isNaN(value) + element.select2('val', "string:#{value}") + else + element.select2('val', "number:#{value}") + diff --git a/app/views/spree/admin/products/index/_filters.html.haml b/app/views/spree/admin/products/index/_filters.html.haml index 0e7d47543d..4493e95258 100644 --- a/app/views/spree/admin/products/index/_filters.html.haml +++ b/app/views/spree/admin/products/index/_filters.html.haml @@ -18,9 +18,7 @@ .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: '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' } diff --git a/spec/features/admin/bulk_product_update_spec.rb b/spec/features/admin/bulk_product_update_spec.rb index 553e2182aa..fdf7d57104 100644 --- a/spec/features/admin/bulk_product_update_spec.rb +++ b/spec/features/admin/bulk_product_update_spec.rb @@ -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 "/admin/products/#{p1.permalink}/edit" 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 "/admin/products/#{p1.permalink}/edit?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 "/admin/products/#{v1.product.permalink}/variants/#{v1.id}/edit" + 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 "/admin/products/#{v1.product.permalink}/variants/#{v1.id}/edit?producerFilter=#{p1.supplier.id}" end end diff --git a/spec/javascripts/unit/admin/bulk_product_update_spec.js.coffee b/spec/javascripts/unit/admin/bulk_product_update_spec.js.coffee index d483bec6a8..d6f7456856 100644 --- a/spec/javascripts/unit/admin/bulk_product_update_spec.js.coffee +++ b/spec/javascripts/unit/admin/bulk_product_update_spec.js.coffee @@ -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 = windowStub = null beforeEach -> module "ofn.admin" @@ -267,15 +267,36 @@ describe "AdminProductEditCtrl", -> DirtyProducts = _DirtyProducts_ DisplayProperties = _DisplayProperties_ - $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.query).toBe query + expect($scope.producerFilter).toBe producerFilter + expect($scope.categoryFilter).toBe categoryFilter + expect($scope.sorting).toBe sorting + expect($scope.importDateFilter).toBe importDateFilter + describe "fetching products", -> $q = null deferred = null @@ -295,6 +316,28 @@ describe "AdminProductEditCtrl", -> $scope.$digest() expect($scope.resetProducts).toHaveBeenCalled() + it "updates url wihth filter after data has been received", inject ($location, $window) -> + query = 'lala' + producerFilter = 2 + categoryFilter = 5 + sorting = 'name desc' + importDateFilter = '2020-06-08' + + $scope.query = query + $scope.producerFilter = producerFilter + $scope.categoryFilter = categoryFilter + $scope.sorting = sorting + $scope.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,7 +936,76 @@ 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.query = query + $scope.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", ->