paging done, needs some additional refactoring

This commit is contained in:
Edin Dazdarevic
2015-02-15 14:21:50 +01:00
parent ba22ea8bd7
commit f675f8b024
11 changed files with 136 additions and 32 deletions

View File

@@ -16,6 +16,7 @@ before do
headers 'Access-Control-Allow-Origin' => 'http://localhost:3001',
'Access-Control-Allow-Methods' => ['OPTIONS', 'GET', 'POST','PUT'],
'Access-Control-Allow-Headers' => 'Origin, X-Requested-With, Content-Type, Accept',
'Access-Control-Expose-Headers' => 'X-Total-Count',
'Access-Control-Allow-Credentials' => 'true'
request.body.rewind

View File

@@ -3,6 +3,10 @@ def get_querystring_hash()
Rack::Utils.parse_nested_query(request.query_string)
end
def add_total_count_header(total)
response.headers['X-Total-Count'] = total
end
# converts list of parameters to array of integers
def mass_to_i(*id_strings)
id_strings.map(&:to_i)

View File

@@ -21,7 +21,7 @@ end
def filter_by_traits(items)
get_querystring_hash.each do |k,v|
items = items.where(["traits ->> '#{k}' = '#{v}'"])
items = items.where(["traits ->> ? = ?", k, v])
end
items
end
@@ -54,9 +54,12 @@ get '/item/category/:category_id/offset/:offset/limit/:limit' do |category_id_s,
input_invalid = offset_and_limit_invalid?(offset,limit) or category_id <= 0
return [].to_json if input_invalid
all_in_cat = filter_by_traits(Item.all_in_category(category_id))
items = Item.best_selling_in_category(category_id, offset,limit)
items = filter_by_traits(items)
add_total_count_header(all_in_cat.count)
prepare_items_for_mass_display(items)
end

View File

@@ -10,7 +10,13 @@ class Item < ActiveRecord::Base
# TODO: change "best selling" algorithm when get some data - currently it's only sorted by earnings
scope :best_selling, -> (offset, limit) { where(:on_display => true).order("(list_price - current_input_price) DESC").limit(limit).offset(offset) }
scope :best_selling_in_sub_category, -> (sub_category_id, offset, limit) { best_selling(offset, limit).where(sub_category_id: sub_category_id) }
scope :best_selling_in_category, -> (category_id, offset, limit) { best_selling(offset, limit).joins( sub_category: [:category] ).where(["category_id = ?", category_id]) }
scope :best_selling_in_section, -> (section_id, offset, limit) { best_selling(offset, limit).joins( sub_category: [category: [ :section ]] ).where( ["section_id = ?", section_id] ) }
scope :all_in_category, -> (category_id) { where(on_display: true).joins( sub_category: [:category]).where(["category_id = ?", category_id]) }
end

View File

@@ -1,6 +1,7 @@
class SubCategory < ActiveRecord::Base
belongs_to :category
has_many :filter_criterias, as: :owner
has_many :items
end

View File

@@ -4,11 +4,13 @@ var ItemConstants = require('../constants/itemConstants');
// Define action methods
var ItemActions = {
loadByCategory: function(categoryId, query) {
loadByCategory: function(categoryId, offset, limit, query) {
AppDispatcher.handleAction({
actionType: ItemConstants.LOAD_BY_CATEGORY,
categoryId : categoryId,
query : query
query : query,
offset : offset,
limit: limit
});
},

View File

@@ -21,20 +21,30 @@ var NavigationActions = {
});
},
goToCategory: function(category,section, query) {
goToCategory: function(category,section, query, offset, limit) {
var url ='/sekcija/' + section.get('name') +'/kategorija/'+ category.get('id') + '/' + category.get('name');
var q = '';
var qp = [];
if(query) {
var qp = [];
for(var key in query) {
if (query.hasOwnProperty(key)) {
if (key !== 'offset' && key !== 'limit' && query.hasOwnProperty(key)) {
qp.push(key + '=' + query[key]);
}
}
if (qp.length > 0) {
q = '?' + qp.join('&');
}
}
if (offset !== undefined) {
qp.push('offset='+offset);
}
if (limit !== undefined) {
qp.push('limit='+limit);
}
if (qp.length > 0) {
q = '?' + qp.join('&');
}
AppDispatcher.handleAction({
actionType: NavigationConstants.CHANGE_URL,

View File

@@ -19,7 +19,8 @@ var ByCategory = React.createClass({
return {
category: category,
items: items,
filter :{}
filter :{},
pagination: {}
};
},
filter: {},
@@ -30,16 +31,24 @@ var ByCategory = React.createClass({
var category = this.state.category;
this.filter[fc.field_name] = fcv.filter_value;
NavigationActions.goToCategory(category, section, this.filter);
NavigationActions.goToCategory(category, section, this.filter, 0, this.state.pagination.limit);
},
removeAppliedFilter: function(name) {
delete this.filter[name];
var section = new Section(this.state.category.get('section'));
var category = this.state.category;
NavigationActions.goToCategory(category, section, this.filter);
NavigationActions.goToCategory(category, section, this.filter, 0, this.state.pagination.limit);
},
changePage: function(page) {
var section = new Section(this.state.category.get('section'));
var category = this.state.category;
NavigationActions.goToCategory(category, section, this.filter, parseInt(page) * this.state.pagination.limit, this.state.pagination.limit);
},
render: function() {
var self = this;
return (
<div>
<div className='col-md-2'>
@@ -54,7 +63,7 @@ var ByCategory = React.createClass({
{this.state.category.get('filter_criterias').map(function(fc) {
return (<div>
<div className='h4'>{fc.title}</div>
<div className='h4' style={{color: '#cd3071'}}>{fc.title}</div>
<ul>
{fc.filter_criteria_values.map(function(fcv) {
return (<li>
@@ -68,7 +77,7 @@ var ByCategory = React.createClass({
<div className='col-md-10'>
<h3> Browse products by category : {this.state.category.get('name')}</h3>
<h3> Kategorija - {this.state.category.get('name')}</h3>
Number of items in this category: {this.state.items.length}
<div>
{this.appliedCategoryFiltersArray().map(function(acf) {
@@ -80,7 +89,8 @@ var ByCategory = React.createClass({
</div>
<ItemList items={this.state.items} />
<div> total count is : {this.state.items.totalCount}</div>
<ItemList items={this.state.items} paginationEnabled={true} total={this.state.items.totalCount} limit={this.state.pagination.limit} onPageChange={this.changePage} currentOffset={this.state.pagination.offset} />
</div>
</div>
);
@@ -88,7 +98,7 @@ var ByCategory = React.createClass({
appliedCategoryFiltersArray: function() {
var filters = [];
for(var key in this.state.filter) {
if(this.state.filter.hasOwnProperty(key)) {
if(this.state.filter.hasOwnProperty(key) && key !== 'limit' && key !== 'offset') {
filters.push({name: key, value: this.state.filter[key]});
}
}
@@ -98,21 +108,36 @@ var ByCategory = React.createClass({
var categoryId = this.getParams().id;
this.filter = this.getQuery();
var offset = this.filter.offset || 0;
var limit = this.filter.limit || 30;
this.setState({
filter: this.filter
filter: this.filter,
pagination: {
offset: offset,
limit: limit
}
});
ItemActions.loadByCategory(categoryId, this.filter);
ItemActions.loadByCategory(categoryId, offset, limit, this.filter);
CategoryActions.loadCategoryDetails(categoryId);
},
componentDidMount: function() {
var categoryId = this.getParams().id;
this.filter = this.getQuery();
var offset = this.filter.offset || 0;
var limit = this.filter.limit || 30;
this.setState({
filter: this.filter
filter: this.filter,
pagination: {
offset: offset,
limit: limit
}
});
ItemActions.loadByCategory(categoryId, this.getQuery());
ItemActions.loadByCategory(categoryId, offset, limit, this.getQuery());
CategoryActions.loadCategoryDetails(categoryId);
ItemStore.addChangeListener(this._onChange);

View File

@@ -3,7 +3,12 @@ var SingleItem = require('./singleItem');
var ItemCollection = require('../../models/itemCollection.js');
var ItemList = React.createClass({
changePage: function(page, e) {
e.preventDefault();
if(this.props.onPageChange) {
this.props.onPageChange(page);
}
},
render: function() {
var items = this.props.items.models.map( function(item) {
@@ -18,10 +23,50 @@ var ItemList = React.createClass({
<ul className="item_list">
{items}
</ul>
{this.getPages()}
</div>
</div>
);
}
},
getPages: function() {
if (!this.props.paginationEnabled) {
return "";
}
var nrOfPages = Math.ceil(this.props.total/ this.props.limit);
if (nrOfPages === 1) {
return "";
}
var maxSlots = 3;
var selectedIndex = Math.floor(this.props.currentOffset / this.props.limit);
var start, end;
start = selectedIndex - Math.floor(maxSlots / 2);
end = selectedIndex + Math.floor(maxSlots / 2 ) + 1;
if (start < 0) start = 0;
if (end > nrOfPages) end = nrOfPages;
if ((end - start) < maxSlots) {
start = Math.max(0, end - maxSlots);
end = Math.min(nrOfPages, start + maxSlots);
}
var pages = [];
for(var i = start; i < end; i++) {
var cn = i === selectedIndex ? "active": "";
pages.push(<li className={cn}><a onClick={this.changePage.bind(this, i)}href="#">{i + 1}</a></li>)
}
return (
<nav>
<ul className="pagination">
{pages}
</ul>
</nav>)
}
});

View File

@@ -13,7 +13,9 @@ var ItemCollection = Backbone.Collection.extend({
}
);
},
setTotalCount: function(total) {
this.totalCount = total;
},
addFilter: function(name, value) {
this.filters = this.filters || {};
this.filters[name] = value;
@@ -75,4 +77,4 @@ var ItemCollection = Backbone.Collection.extend({
}
});
module.exports = ItemCollection;
module.exports = ItemCollection;

View File

@@ -51,24 +51,30 @@ var fetchItemWithDetails = function() {
}
}
var fetchItemsByCategory = function(categoryId, query) {
var items = _itemsByCategory;
var fetchItemsByCategory = function(categoryId, offset, limit, query) {
//var items = _itemsByCategory;
var items = new ItemCollection();
items.clearFilter();
items.setClassificationType(2);
items.setClassificationId(categoryId);
items.setLimit(30);
items.setOffset(0);
items.setLimit(limit);
items.setOffset(offset);
for(var key in query) {
if (query.hasOwnProperty(key)) {
if (query.hasOwnProperty(key) && key != 'limit' && key !='offset') {
items.addFilter(key, query[key]);
}
}
items.fetch({
success: function() {
success: function(collection, response, options) {
var total = options.xhr.getResponseHeader('x-total-count');
items.setTotalCount(total);
ItemStore.emitChange();
}});
_itemsByCategory = items;
};
var fetchBestSellingItemsForSection = function(sectionId) {
@@ -78,7 +84,6 @@ var fetchBestSellingItemsForSection = function(sectionId) {
items.setClassificationId(sectionId);
items.setLimit(30);
items.setOffset(0);
items.fetch({
success: function() {
@@ -147,7 +152,7 @@ AppDispatcher.register(function(payload) {
loadItemsForFrontpage();
break;
case ItemConstants.LOAD_BY_CATEGORY:
fetchItemsByCategory(action.categoryId, action.query);
fetchItemsByCategory(action.categoryId, action.offset, action.limit, action.query);
break;
default: