BPUR: Add ability to filter by search and toggle variants

This commit is contained in:
Rob H
2013-06-17 12:46:03 +05:30
committed by Rohan Mitchell
parent 57f2eff1b0
commit b775145b16
6 changed files with 94 additions and 36 deletions

View File

@@ -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 = {};

View File

@@ -1,3 +1,7 @@
#product_distributors_field span {
display: block;
}
th.firstcol, td.firstcol {
border-left: 1px solid #cee1f4;
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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" } ] } ] );
});
});
});