mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-02-12 23:27:48 +00:00
BPUR: Add ability to filter by search and toggle variants
This commit is contained in:
@@ -74,6 +74,27 @@ productsApp.directive('ngTrackVariant', function(){
|
||||
}
|
||||
});
|
||||
|
||||
productsApp.directive('ngToggleVariants',function(){
|
||||
return {
|
||||
link: function(scope,element,attrs){
|
||||
element.bind('click', function(){
|
||||
scope.$apply(function(){
|
||||
if (scope.displayProperties[scope.product.id].showVariants){
|
||||
scope.displayProperties[scope.product.id].showVariants = false;
|
||||
element.removeClass('icon-chevron-down');
|
||||
element.addClass('icon-chevron-right');
|
||||
}
|
||||
else {
|
||||
scope.displayProperties[scope.product.id].showVariants = true;
|
||||
element.removeClass('icon-chevron-right');
|
||||
element.addClass('icon-chevron-down');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
productsApp.controller('AdminBulkProductsCtrl', function($scope, $timeout, $http, dataFetcher) {
|
||||
$scope.dirtyProducts = {};
|
||||
|
||||
@@ -90,7 +111,11 @@ productsApp.controller('AdminBulkProductsCtrl', function($scope, $timeout, $http
|
||||
|
||||
$scope.refreshProducts = function(){
|
||||
dataFetcher('/admin/products/bulk_index.json').then(function(data){
|
||||
$scope.products = toObjectWithIDKeys(data);
|
||||
$scope.products = data;
|
||||
$scope.displayProperties = {};
|
||||
angular.forEach($scope.products,function(product){
|
||||
$scope.displayProperties[product.id] = { showVariants: false }
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
@@ -111,8 +136,9 @@ productsApp.controller('AdminBulkProductsCtrl', function($scope, $timeout, $http
|
||||
url: '/admin/products/'+product.permalink_live+".js"
|
||||
})
|
||||
.success(function(data){
|
||||
delete $scope.products[product.id]
|
||||
if ($scope.dirtyProducts.hasOwnProperty(product.id)) delete $scope.dirtyProducts[product.id]
|
||||
$scope.products.splice($scope.products.indexOf(product),1);
|
||||
if ($scope.dirtyProducts.hasOwnProperty(product.id)) delete $scope.dirtyProducts[product.id];
|
||||
$scope.displayDirtyProducts();
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -124,8 +150,9 @@ productsApp.controller('AdminBulkProductsCtrl', function($scope, $timeout, $http
|
||||
url: '/admin/products/'+product.permalink_live+"/variants/"+variant.id+".js"
|
||||
})
|
||||
.success(function(data){
|
||||
delete $scope.products[product.id].variants[variant.id]
|
||||
if ($scope.dirtyProducts.hasOwnProperty(product.id) && $scope.dirtyProducts[product.id].hasOwnProperty("variants") && $scope.dirtyProducts[product.id].variants.hasOwnProperty(variant.id)) delete $scope.dirtyProducts[product.id].variants[variant.id]
|
||||
product.variants.splice(product.variants.indexOf(variant),1);
|
||||
if ($scope.dirtyProducts.hasOwnProperty(product.id) && $scope.dirtyProducts[product.id].hasOwnProperty("variants") && $scope.dirtyProducts[product.id].variants.hasOwnProperty(variant.id)) delete $scope.dirtyProducts[product.id].variants[variant.id];
|
||||
$scope.displayDirtyProducts();
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -138,8 +165,7 @@ productsApp.controller('AdminBulkProductsCtrl', function($scope, $timeout, $http
|
||||
var id = data.product.id;
|
||||
dataFetcher("/admin/products/bulk_index.json?q[id_eq]="+id).then(function(data){
|
||||
var newProduct = data[0];
|
||||
newProduct.variants = toObjectWithIDKeys(newProduct.variants)
|
||||
$scope.products[newProduct.id] = newProduct;
|
||||
$scope.products.push(newProduct);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -156,7 +182,6 @@ productsApp.controller('AdminBulkProductsCtrl', function($scope, $timeout, $http
|
||||
data: productsToSubmit
|
||||
})
|
||||
.success(function(data){
|
||||
data = toObjectWithIDKeys(data);
|
||||
if (angular.toJson($scope.products) == angular.toJson(data)){
|
||||
$scope.products = data;
|
||||
$scope.dirtyProducts = {};
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
#product_distributors_field span {
|
||||
display: block;
|
||||
}
|
||||
|
||||
th.firstcol, td.firstcol {
|
||||
border-left: 1px solid #cee1f4;
|
||||
}
|
||||
|
||||
@@ -11,11 +11,17 @@
|
||||
|
||||
%div#new_product(data-hook)
|
||||
|
||||
%div{ 'ng-app' => 'bulk_product_update', 'ng-controller' => 'AdminBulkProductsCtrl', 'ng-init' => 'refreshSuppliers(); refreshProducts()' }
|
||||
%div{ 'ng-app' => 'bulk_product_update', 'ng-controller' => 'AdminBulkProductsCtrl', 'ng-init' => 'refreshSuppliers(); refreshProducts();' }
|
||||
%div.options
|
||||
Filter Results:
|
||||
%input.search{ 'ng-model' => 'query', :type => 'text', 'placeholder' => 'Search Value' }
|
||||
%br.clear
|
||||
%br.clear
|
||||
%table.index#listing_products
|
||||
%thead
|
||||
%tr
|
||||
%th Name
|
||||
%th.actions
|
||||
%th.firstcol Name
|
||||
%th Supplier
|
||||
%th Price
|
||||
%th On Hand
|
||||
@@ -23,7 +29,9 @@
|
||||
%th.actions
|
||||
%tbody{ 'ng-repeat' => 'product in products | filter:query' }
|
||||
%tr
|
||||
%td
|
||||
%td.actions
|
||||
%a{ 'ng-toggle-variants' => 'true', :class => "view-variants icon-chevron-right", 'ng-show' => 'hasVariants(product)' }
|
||||
%td.firstcol
|
||||
%input{ 'ng-model' => "product.name", :name => 'product_name', 'ng-track-product' => 'name', :type => 'text' }
|
||||
%td
|
||||
%select.select2{ 'ng-model' => 'product.supplier_id', :name => 'supplier_id', 'ng-track-product' => 'supplier_id', 'ng-options' => 's.id as s.name for s in suppliers' }
|
||||
@@ -38,8 +46,10 @@
|
||||
%a{ 'ng-click' => 'editWarn(product)', :class => "edit-product icon-edit no-text" }
|
||||
%a{ 'ng-click' => 'cloneProduct(product)', :class => "clone-product icon-copy no-text" }
|
||||
%a{ 'ng-click' => 'deleteProduct(product)', :class => "delete-product icon-trash no-text" }
|
||||
%tr{ 'ng-repeat' => 'variant in product.variants' }
|
||||
%td
|
||||
%tr{ 'ng-repeat' => 'variant in product.variants', 'ng-show' => 'displayProperties[product.id].showVariants' }
|
||||
%td.actions
|
||||
%a{ :class => "variant-item icon-caret-right" }
|
||||
%td.firstcol
|
||||
{{ variant.options_text }}
|
||||
%td
|
||||
%td
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
r.list_of :products, @collection do
|
||||
r.list_of :products, @collection.sort_by(&:id) do
|
||||
r.element :id
|
||||
r.element :name
|
||||
r.element :supplier_id
|
||||
|
||||
@@ -110,6 +110,8 @@ feature %q{
|
||||
v2 = FactoryGirl.create(:variant)
|
||||
|
||||
visit '/admin/products/bulk_index'
|
||||
page.should have_selector "a.view-variants"
|
||||
first("a.view-variants").click
|
||||
|
||||
page.should have_field "product_name", with: v1.product.name
|
||||
page.should have_field "product_name", with: v2.product.name
|
||||
@@ -213,6 +215,8 @@ feature %q{
|
||||
login_to_admin_section
|
||||
|
||||
visit '/admin/products/bulk_index'
|
||||
page.should have_selector "a.view-variants"
|
||||
first("a.view-variants").click
|
||||
|
||||
page.should have_field "variant_price", with: "3.0"
|
||||
page.should have_field "variant_on_hand", with: "9"
|
||||
@@ -227,6 +231,8 @@ feature %q{
|
||||
page.find("span#update-status-message").should have_content "Update complete"
|
||||
|
||||
visit '/admin/products/bulk_index'
|
||||
page.should have_selector "a.view-variants"
|
||||
first("a.view-variants").click
|
||||
|
||||
page.should have_field "variant_price", with: "4.0"
|
||||
page.should have_field "variant_on_hand", with: "10"
|
||||
@@ -239,6 +245,8 @@ feature %q{
|
||||
login_to_admin_section
|
||||
|
||||
visit '/admin/products/bulk_index'
|
||||
page.should have_selector "a.view-variants"
|
||||
first("a.view-variants").click
|
||||
|
||||
page.should have_field "variant_price", with: "3.0"
|
||||
|
||||
@@ -248,6 +256,8 @@ feature %q{
|
||||
page.find("span#update-status-message").should have_content "Update complete"
|
||||
|
||||
visit '/admin/products/bulk_index'
|
||||
page.should have_selector "a.view-variants"
|
||||
first("a.view-variants").click
|
||||
|
||||
page.should have_field "variant_price", with: "10.0"
|
||||
end
|
||||
@@ -291,6 +301,7 @@ feature %q{
|
||||
first("a.delete-product").click
|
||||
page.driver.browser.switch_to.alert.accept
|
||||
|
||||
page.should have_selector "a.delete-product"
|
||||
page.should have_selector "a.delete-product", :count => 2
|
||||
#page.should have_selector "div.flash.notice", text: "Product has been deleted."
|
||||
|
||||
@@ -306,6 +317,8 @@ feature %q{
|
||||
login_to_admin_section
|
||||
|
||||
visit '/admin/products/bulk_index'
|
||||
page.should have_selector "a.view-variants"
|
||||
all("a.view-variants").each{ |e| e.click }
|
||||
|
||||
page.should have_selector "a.delete-variant", :count => 3
|
||||
|
||||
@@ -316,6 +329,8 @@ feature %q{
|
||||
#page.should have_selector "div.flash.notice", text: "Product has been deleted."
|
||||
|
||||
visit '/admin/products/bulk_index'
|
||||
page.should have_selector "a.view-variants"
|
||||
all("a.view-variants").select{ |e| e.visible? }.each{ |e| e.click }
|
||||
|
||||
page.should have_selector "a.delete-variant", :count => 2
|
||||
end
|
||||
@@ -344,6 +359,8 @@ feature %q{
|
||||
login_to_admin_section
|
||||
|
||||
visit '/admin/products/bulk_index'
|
||||
page.should have_selector "a.view-variants"
|
||||
first("a.view-variants").click
|
||||
|
||||
page.should have_selector "a.edit-variant", :count => 3
|
||||
|
||||
@@ -368,6 +385,11 @@ feature %q{
|
||||
|
||||
page.should have_selector "a.clone-product", :count => 4
|
||||
page.should have_field "product_name", with: "COPY OF #{p1.name}"
|
||||
|
||||
visit '/admin/products/bulk_index'
|
||||
|
||||
page.should have_selector "a.clone-product", :count => 4
|
||||
page.should have_field "product_name", with: "COPY OF #{p1.name}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -206,12 +206,11 @@ describe("AdminBulkProductsCtrl", function(){
|
||||
expect(scope.suppliers).toEqual("list of suppliers");
|
||||
});
|
||||
|
||||
it("gets a list of products which is then passed to toObjectWithIDKeys()", function(){
|
||||
it("gets a list of products", function(){
|
||||
httpBackend.expectGET('/admin/products/bulk_index.json').respond("list of products");
|
||||
spyOn(window, "toObjectWithIDKeys").andReturn("product object with ids as keys")
|
||||
scope.refreshProducts();
|
||||
httpBackend.flush();
|
||||
expect(scope.products).toEqual("product object with ids as keys");
|
||||
expect(scope.products).toEqual("list of products");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -289,7 +288,7 @@ describe("AdminBulkProductsCtrl", function(){
|
||||
|
||||
it("runs displaySuccess() when post returns success",function(){
|
||||
spyOn(scope, "displaySuccess");
|
||||
scope.products = { 1: { id: 1, name: "P1" }, 2: { id: 2, name: "P2" } };
|
||||
scope.products = [ { id: 1, name: "P1" }, { id: 2, name: "P2" } ];
|
||||
httpBackend.expectPOST('/admin/products/bulk_update').respond(200, [ { id: 1, name: "P1" }, { id: 2, name: "P2" } ] );
|
||||
scope.updateProducts("list of dirty products");
|
||||
httpBackend.flush();
|
||||
@@ -323,20 +322,20 @@ describe("AdminBulkProductsCtrl", function(){
|
||||
|
||||
it("deletes products with a http delete request to /admin/products/(permalink).js", function(){
|
||||
spyOn(window, "confirm").andReturn(true);
|
||||
scope.products = { 9: { id: 9, permalink_live: "apples" }, 13: { id: 13, permalink_live: "oranges" } };
|
||||
scope.products = [ { id: 9, permalink_live: "apples" }, { id: 13, permalink_live: "oranges" } ];
|
||||
httpBackend.expectDELETE('/admin/products/oranges.js').respond(200, "data");
|
||||
scope.deleteProduct(scope.products[13]);
|
||||
scope.deleteProduct(scope.products[1]);
|
||||
httpBackend.flush();
|
||||
});
|
||||
|
||||
it("removes the specified product from both scope.products and scope.dirtyProducts (if it exists there)", function(){
|
||||
spyOn(window, "confirm").andReturn(true);
|
||||
scope.products = { 9: { id: 9, permalink_live: "apples" }, 13: { id: 13, permalink_live: "oranges" } };
|
||||
scope.products = [ { id: 9, permalink_live: "apples" }, { id: 13, permalink_live: "oranges" } ];
|
||||
scope.dirtyProducts = { 9: { id: 9, someProperty: "something" }, 13: { id: 13, name: "P1" } };
|
||||
httpBackend.expectDELETE('/admin/products/oranges.js').respond(200, "data");
|
||||
scope.deleteProduct(scope.products[13]);
|
||||
scope.deleteProduct(scope.products[1]);
|
||||
httpBackend.flush();
|
||||
expect(scope.products).toEqual( { 9: { id: 9, permalink_live: "apples" } } );
|
||||
expect(scope.products).toEqual( [ { id: 9, permalink_live: "apples" } ] );
|
||||
expect(scope.dirtyProducts).toEqual( { 9: { id: 9, someProperty: "something" } } );
|
||||
});
|
||||
});
|
||||
@@ -348,20 +347,20 @@ describe("AdminBulkProductsCtrl", function(){
|
||||
|
||||
it("deletes variants with a http delete request to /admin/products/(permalink)/variants/(variant_id).js", function(){
|
||||
spyOn(window, "confirm").andReturn(true);
|
||||
scope.products = { 9: { id: 9, permalink_live: "apples", variants: { 3: { id: 3, price: 12 } } }, 13: { id: 13, permalink_live: "oranges" } };
|
||||
scope.products = [ { id: 9, permalink_live: "apples", variants: [ { id: 3, price: 12 } ] }, { id: 13, permalink_live: "oranges" } ];
|
||||
httpBackend.expectDELETE('/admin/products/apples/variants/3.js').respond(200, "data");
|
||||
scope.deleteVariant(scope.products[9],scope.products[9].variants[3]);
|
||||
scope.deleteVariant(scope.products[0],scope.products[0].variants[0]);
|
||||
httpBackend.flush();
|
||||
});
|
||||
|
||||
it("removes the specified variant from both the variants object and scope.dirtyProducts (if it exists there)", function(){
|
||||
spyOn(window, "confirm").andReturn(true);
|
||||
scope.products = { 9: { id: 9, permalink_live: "apples", variants: { 3: { id: 3, price: 12.0 }, 4: { id: 4, price: 6.0 } } }, 13: { id: 13, permalink_live: "oranges" } };
|
||||
scope.products = [ { id: 9, permalink_live: "apples", variants: [ { id: 3, price: 12.0 }, { id: 4, price: 6.0 } ] }, { id: 13, permalink_live: "oranges" } ];
|
||||
scope.dirtyProducts = { 9: { id: 9, variants: { 3: { id: 3, price: 12.0 }, 4: { id: 4, price: 6.0 } } }, 13: { id: 13, name: "P1" } };
|
||||
httpBackend.expectDELETE('/admin/products/apples/variants/3.js').respond(200, "data");
|
||||
scope.deleteVariant(scope.products[9],scope.products[9].variants[3]);
|
||||
scope.deleteVariant(scope.products[0],scope.products[0].variants[0]);
|
||||
httpBackend.flush();
|
||||
expect(scope.products[9].variants).toEqual( { 4: { id: 4, price: 6.0 } } );
|
||||
expect(scope.products[0].variants).toEqual( [ { id: 4, price: 6.0 } ] );
|
||||
expect(scope.dirtyProducts).toEqual( { 9: { id: 9, variants: { 4: { id: 4, price: 6.0 } } }, 13: { id: 13, name: "P1" } } );
|
||||
});
|
||||
});
|
||||
@@ -372,22 +371,20 @@ describe("AdminBulkProductsCtrl", function(){
|
||||
});
|
||||
|
||||
it("clones products using a http get request to /admin/products/(permalink)/clone.json", function(){
|
||||
scope.products = { 13: { id: 13, permalink_live: "oranges" } }
|
||||
scope.products = [ { id: 13, permalink_live: "oranges" } ];
|
||||
httpBackend.expectGET('/admin/products/oranges/clone.json').respond(200, { product: { id: 17, name: "new_product" } } );
|
||||
httpBackend.expectGET('/admin/products/bulk_index.json?q[id_eq]=17').respond(200, [ { id: 17, name: "new_product" } ] );
|
||||
scope.cloneProduct(scope.products[13]);
|
||||
scope.cloneProduct(scope.products[0]);
|
||||
httpBackend.flush();
|
||||
});
|
||||
|
||||
it("adds the newly created product to scope.products, sending variants to toObjectWithIDKeys()", function(){
|
||||
spyOn(window, "toObjectWithIDKeys").andCallThrough();
|
||||
scope.products = { 13: { id: 13, permalink_live: "oranges" } };
|
||||
it("adds the newly created product to scope.products", function(){
|
||||
scope.products = [ { id: 13, permalink_live: "oranges" } ];
|
||||
httpBackend.expectGET('/admin/products/oranges/clone.json').respond(200, { product: { id: 17, name: "new_product", variants: [ { id: 3, name: "V1" } ] } } );
|
||||
httpBackend.expectGET('/admin/products/bulk_index.json?q[id_eq]=17').respond(200, [ { id: 17, name: "new_product", variants: [ { id: 3, name: "V1" } ] } ] );
|
||||
scope.cloneProduct(scope.products[13]);
|
||||
scope.cloneProduct(scope.products[0]);
|
||||
httpBackend.flush();
|
||||
expect(toObjectWithIDKeys).toHaveBeenCalledWith([ { id: 3, name: "V1" } ])
|
||||
expect(scope.products).toEqual( { 13: { id: 13, permalink_live: "oranges" }, 17: { id: 17, name: "new_product", variants: { 3: { id: 3, name: "V1" } } } } );
|
||||
expect(scope.products).toEqual( [ { id: 13, permalink_live: "oranges" }, { id: 17, name: "new_product", variants: [ { id: 3, name: "V1" } ] } ] );
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user