- browsing by subcategory

This commit is contained in:
Edin Dazdarevic
2015-03-29 23:23:21 +02:00
parent 8e3557cf2e
commit 7c1eb2f4fd
12 changed files with 349 additions and 10 deletions

View File

@@ -66,11 +66,18 @@ end
# gets items in sub category ( useful for page showing single sub_category )
get '/item/sub_category/:sub_category_id/offset/:offset/limit/:limit' do |sub_category_id_s, offset_s, limit_s|
sub_category_id, offset, limit = mass_to_i(sub_category_id_s, offset_s, limit_s)
input_invalid = offset_and_limit_invalid?(offset,limit) or sub_category_id <= 0
return [].to_json if input_invalid
all_in_sub_cat = filter_by_traits(Item.all_in_sub_category(sub_category_id))
items = Item.best_selling_in_sub_category(sub_category_id, offset, limit)
items = filter_by_traits(items)
add_total_count_header(all_in_sub_cat.count)
prepare_items_for_mass_display(items)
end

View File

@@ -0,0 +1,18 @@
get '/subcategory' do
SubCategory
.eager_load(category: :section)
.order(:order)
.all
.to_json(:include => [filter_criterias: {include: :filter_criteria_values}, category: {include: :section} ])
end
get '/subcategory/:id' do
id = params[:id].to_i
SubCategory
.eager_load(category: :section)
.order(:order)
.find(id)
.to_json(:include => [filter_criterias: {include: :filter_criteria_values}, category: {include: :section} ])
end

View File

@@ -20,4 +20,5 @@ class Item < ActiveRecord::Base
scope :all_in_category, -> (category_id) { where(on_display: true).joins( sub_category: [:category]).where(["category_id = ?", category_id]) }
scope :all_in_sub_category, -> (sub_category_id) { where(on_display: true).where(["sub_category_id = ?", sub_category_id]) }
end

View File

@@ -0,0 +1,30 @@
var AppDispatcher = require('../dispatcher/appDispatcher');
var BySubCategoryConstants = require('../constants/bySubCategoryConstants');
// Define action methods
var BySubCategoryActions = {
load: function(subCategoryId, offset, limit, filter) {
AppDispatcher.handleAction({
actionType: BySubCategoryConstants.LOAD,
subCategoryId : subCategoryId,
offset: offset,
limit: limit,
filter: filter
});
},
filterCriteriaClick: function(fc, fcv) {
AppDispatcher.handleAction({
actionType: BySubCategoryConstants.FILTER_CRITERIA_CLICK,
fc: fc,
fcv: fcv
});
},
removeAppliedFilter: function(name) {
AppDispatcher.handleAction({
actionType: BySubCategoryConstants.REMOVE_APPLIED_FILTER,
name: name
});
},
};
module.exports = BySubCategoryActions;

View File

@@ -3,7 +3,6 @@ var NavigationConstants = require('../constants/navigationConstants');
// Define action methods
var NavigationActions = {
// select item
goToItemDetails: function(item) {
console.log("Going to item details");
@@ -20,10 +19,7 @@ var NavigationActions = {
url: '/sekcija/'+ section.get('id') + '/' + section.get('name')
});
},
goToCategory: function(category,section, query, offset, limit) {
var url ='/sekcija/' + section.get('name') +'/kategorija/'+ category.get('id') + '/' + category.get('name');
_getQueryStringPart: function(query, offset, limit) {
var q = '';
var qp = [];
@@ -46,17 +42,25 @@ var NavigationActions = {
if (qp.length > 0) {
q = '?' + qp.join('&');
}
return q;
},
goToCategory: function(category,section, query, offset, limit) {
var url ='/sekcija/' + section.get('name') +'/kategorija/'+ category.get('id') + '/' + category.get('name');
var q = this._getQueryStringPart(query, offset, limit);
AppDispatcher.handleAction({
actionType: NavigationConstants.CHANGE_URL,
url: (url + q)
});
},
goToSubCategory: function(subCategory) {
// TODO: implement when ready
goToSubCategory: function(subCategory, offset, limit, query) {
var q = this._getQueryStringPart(query, offset, limit);
var url = '/podkategorija/' + subCategory.get('id') + '/' + subCategory.get('name');
AppDispatcher.handleAction({
actionType: NavigationConstants.CHANGE_URL,
url: '/'
url: (url + q)
});
},

View File

@@ -1,6 +1,7 @@
var React = require('react'),
Router = require('react-router'),
Category = require('../../models/category'),
SubCategory = require('../../models/subCategory'),
Section = require('../../models/section'),
ItemCollection = require('../../models/itemCollection'),
ItemActions = require('../../actions/itemActions.js'),
@@ -10,7 +11,7 @@ var React = require('react'),
NavigationStore = require('../../stores/navigationStore'),
ItemList = require('../items/itemList'),
NavigationActions = require('../../actions/navigationActions'),
LinkBanner = require('../linkBanner/linkBanner'),
LinkBanner = require('../linkBanner/linkBanner'),
Globals = require('../../globals');
var FilterCriteriaSelector = require('./filterCriteriaSelector');
@@ -45,6 +46,11 @@ var ByCategory = React.createClass({
NavigationActions.goToCategory(category, section, this.filter, 0, this.state.pagination.limit);
},
onSCClick: function(sc) {
var subCategory = new SubCategory(sc);
NavigationActions.goToSubCategory(subCategory, 0, Globals.defaultLimit);
event.preventDefault();
},
changePage: function(page) {
var section = new Section(this.state.category.get('section'));
var category = this.state.category;
@@ -60,7 +66,7 @@ var ByCategory = React.createClass({
<div>Podkategorije</div>
<ul>
{(this.state.category.get('sub_categories') || []).map(function(sc) {
return (<li> <a>{sc.name}</a></li>)
return (<li> <a onClick={self.onSCClick.bind(self, sc)}>{sc.name}</a></li>)
})}
</ul>

View File

@@ -0,0 +1,80 @@
var React = require('react'),
Router = require('react-router');
var BySubCategoryActions = require('../../actions/bySubCategoryActions');
var BySubCategoryStore = require('../../stores/bySubCategoryStore');
var ItemStore = require('../../stores/itemStore');
var CategoryStore = require('../../stores/categoryStore');
var Globals = require('../../globals');
var FilterCriteriaSelector = require('./filterCriteriaSelector');
var AppliedFiltersList = require('./appliedFiltersList');
var NavigationActions = require('../../actions/navigationActions');
var ItemList = require('../items/itemList');
var BySubCategory = React.createClass({
mixins: [Router.State],
getInitialState : function() {
return BySubCategoryStore.getState();
},
onFCClick: function(fc, fcv) {
BySubCategoryActions.filterCriteriaClick(fc, fcv);
},
removeAppliedFilter: function(name) {
BySubCategoryActions.removeAppliedFilter(name);
},
render : function() {
return (<div>
<div className='col-md-2'>
<FilterCriteriaSelector filterCriterias={this.state.subCategory.get('filter_criterias')} onFCClick={this.onFCClick} />
</div>
<div classname='col-md-10'>
Podkategorija {this.state.subCategory.get('name')}
Number of items in this subcategory: {this.state.items.length}
<div>
<AppliedFiltersList filters={this.appliedSubCategoryFiltersArray()} onRemove={this.removeAppliedFilter} />
</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>)
},
appliedSubCategoryFiltersArray: function() {
var filters = [];
for(var key in this.state.filter) {
if(this.state.filter.hasOwnProperty(key) && key !== 'limit' && key !== 'offset') {
filters.push({name: key, value: this.state.filter[key]});
}
}
return filters;
},
update: function() {
var subCategoryId = this.getParams().id;
var filter = this.getQuery();
var offset = filter.offset || 0;
var limit = filter.limit || Globals.DefaultPageSize;
BySubCategoryActions.load(subCategoryId, offset, limit, filter);
},
componentWillReceiveProps: function() {
this.update();
},
componentDidMount: function() {
BySubCategoryStore.addChangeListener(this._onChange);
this.update();
},
componentWillUnmount: function() {
BySubCategoryStore.removeChangeListener(this._onChange);
},
_onChange: function() {
if(this.isMounted()) {
this.setState(BySubCategoryStore.getState());
}
}
});
module.exports = BySubCategory;

View File

@@ -0,0 +1,8 @@
var keyMirror = require('react/lib/keyMirror');
// Define action constants
module.exports = keyMirror({
LOAD: null,
FILTER_CRITERIA_CLICK: null,
REMOVE_APPLIED_FILTER: null
});

View File

@@ -0,0 +1,13 @@
var Backbone = require('backbone');
var Globals = require('../globals');
var SubCategory = Backbone.Model.extend({
urlRoot : Globals.ApiUrl + '/subcategory',
defaults : {
name: '',
filter_criterias: []
}
});
module.exports = SubCategory;

View File

@@ -14,6 +14,7 @@ var CheckoutPage = require('./components/cart/checkoutPage');
var RootApp = require('./components/rootApp');
var StartPage = require('./components/startPage/startPage');
var ByCategory = require('./components/browsing/byCategory');
var BySubCategory = require('./components/browsing/bySubCategory');
var BySection = require('./components/browsing/bySection');
var ThankYouPage = require('./components/thankyou/thankYouPage');
@@ -31,6 +32,7 @@ var routes = (
<Route name='dostava' path="/dostava" handler={CheckoutPage} />
<Route name='registracija' path="/registracija" handler={Register} />
<Route name='login' path="/login" handler={Login} />
<Route name='podkategorija' path="/podkategorija/:id/*" handler={BySubCategory} />
<Route name='byCat' path="sekcija/:sekcijaName/kategorija/:id/*" handler={ByCategory} />
<Route name='hvala' path="/hvala" handler={ThankYouPage} />
<Route name='pretraga' path="/pretraga" handler={SearchResultsPage} />

View File

@@ -0,0 +1,170 @@
var AppDispatcher = require('../dispatcher/appDispatcher');
var EventEmitter = require('events').EventEmitter;
//var SubCategoryCollection = require('../models/subCategoryCollection');
var SubCategory = require('../models/subCategory');
var NavigationActions = require('../actions/navigationActions');
var BySubCategoryConstants = require('../constants/bySubCategoryConstants');
var ItemCollection = require('../models/itemCollection');
var _ = require('underscore');
var _state = {
subCategory : (new SubCategory()),
items: (new ItemCollection()),
filter: {},
pagination: {}
};
//var _categoryDetails = new Category();
//var loadSections = function() {
//var sections = new SectionCollection();
//sections.fetch({success: function() {
//sectionState.sections = sections.models;
//// change will be called automatically when
//// action is run but we need to emit it again
//// when the data arive
//// it's a bit "unfluxy" but convenient.
//// "true philosophy" would be to run another "data arrived" action
//SectionStore.emitChange();
//}});
//};
var fetchItemsBySubCategory = function(subCategoryId, offset, limit, query) {
//var items = _itemsByCategory;
query = query || {};
var items = new ItemCollection();
items.clearFilter();
items.setClassificationType(3);
items.setClassificationId(subCategoryId);
items.setLimit(limit);
items.setOffset(offset);
for(var key in query) {
if (query.hasOwnProperty(key) && key != 'limit' && key !='offset') {
items.addFilter(key, query[key]);
}
}
items.fetch({
success: function(collection, response, options) {
var total = options.xhr.getResponseHeader('x-total-count');
items.setTotalCount(total);
_state.items = items;
BySubCategoryStore.emitChange();
}});
};
var load = function(subCategoryId, offset, limit, filter) {
var subCategory = new SubCategory({id : subCategoryId});
subCategory.fetch({
success: function() {
_state.subCategory = subCategory;
fetchItemsBySubCategory(subCategoryId, offset, limit, filter);
}
});
_state.filter = filter;
//BySubCategoryStore.emitChange();
};
var handleFilterCriteriaClick = function(fc, fcv) {
_state.filter[fc.field_name] = fcv.filter_value;
setTimeout(function() {
NavigationActions.goToSubCategory(_state.subCategory, 0, _state.pagination.limit, _state.filter);
}, 0);
};
var handleRemoveAppliedFilter= function(name) {
delete _state.filter[name];
setTimeout(function() {
NavigationActions.goToSubCategory(_state.subCategory, 0, _state.pagination.limit, _state.filter);
}, 0);
};
//var loadCategoryDetails = function(categoryId) {
//var category = new Category({id : categoryId});
//category.fetch({
//success: function() {
//_categoryDetails = category;
//BySubCategoryStore.emitChange();
//}
//});
//};
//var setHovered = function(id) {
//sectionState.hoveredSection = id;
//}
// Extend SectionStore with EventEmitter to add eventing capabilities
var BySubCategoryStore = _.extend({}, EventEmitter.prototype, {
getState: function() {
return _state;
},
// Emit Change event
emitChange: function() {
console.log("Emmiting BySubCategory change!");
this.emit('change');
},
// Add change listener
addChangeListener: function(callback) {
this.on('change', callback);
},
// Remove change listener
removeChangeListener: function(callback) {
this.removeListener('change', callback);
}
});
// Register callback with AppDispatcher
AppDispatcher.register(function(payload) {
var action = payload.action;
var text;
switch(action.actionType) {
case BySubCategoryConstants.LOAD:
load(action.subCategoryId, action.offset, action.limit, action.filter);
break;
case BySubCategoryConstants.FILTER_CRITERIA_CLICK:
handleFilterCriteriaClick(action.fc, action.fcv);
break;
case BySubCategoryConstants.REMOVE_APPLIED_FILTER:
handleRemoveAppliedFilter(action.name);
break;
// Respond to SELECT_ITEM action
//case SectionConstants.LOAD_SECTIONS:
//loadSections();
//break;
//case SectionConstants.SET_SECTION_HOVER:
//setHovered(action.section.get('id'));
//break;
//case SectionConstants.UNSET_SECTION_HOVER:
//setHovered('');
//break;
//case CategoryConstants.LOAD_CATEGORY_DETAILS:
//loadCategoryDetails(action.categoryId);
//break;
default:
return true;
}
// If action was responded to, emit change event
BySubCategoryStore.emitChange();
return true;
});
module.exports = BySubCategoryStore;